import traceback
import warnings
import os
import random
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import math
import sys
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure

from PyQt5.QtGui import QColor, QPainterPath
from OS import Ui_OS_pageSwapVisualized
from OSPlus import Ui_Algorithm_Analysis
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtWidgets import QGraphicsDropShadowEffect, QVBoxLayout, QGraphicsRectItem, QGraphicsTextItem, QGraphicsScene, QFrame, QGraphicsOpacityEffect
from PyQt5.QtCore import Qt, pyqtSlot, QPropertyAnimation, QEasingCurve, QRect, QTimer

from OPT import *
from FIFO import main as FIFOMain, FIFO
from LRU import main as LRUMain, LRU
from LFU import main as LFUMain, LFU
from virtualizedP_CMenu import Ui_MainWindow as Ui_PCMenu  # 使用正确的类名


matplotlib.use('Qt5Agg')

# 完全禁用所有警告
if not sys.warnoptions:
    warnings.simplefilter("ignore")
    os.environ["PYTHONWARNINGS"] = "ignore"

class mainUI(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(mainUI, self).__init__(parent)
        
        # 创建UI对象
        self.ui = Ui_OS_pageSwapVisualized()
        self.ui.setupUi(self)
        
        # 设置窗口图标
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("./icons/page.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.setWindowIcon(icon)
        
        # 设置无边框窗口
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        
        # 拖动相关变量
        self._drag_pos = None
        
        # 初始化UI设置
        self.setup_scroll_area()
        
        # 初始化算法选择
        self.select_algorithm = 1  # 默认FIFO
        self.select_way = 1  # 默认随机模式
        
        # 设置滑块指示器
        self.slider_indicator = QFrame(self.ui.frame_8)
        self.slider_indicator.setGeometry(QRect(self.ui.random.x() - 2, 101 - 3, self.ui.random.width(), 3))
        self.slider_indicator.setStyleSheet("background-color: rgb(255, 0, 0);")
        self.slider_indicator.setFrameShape(QFrame.StyledPanel)
        self.slider_indicator.setFrameShadow(QFrame.Raised)
        self.slider_indicator.show()
        
        # 创建滑块动画
        self.slide_animation = QPropertyAnimation(self.slider_indicator, b"geometry")
        self.slide_animation.setDuration(300)
        self.slide_animation.setEasingCurve(QEasingCurve.OutCubic)

        # 性能分析窗口
        self.analysis_window = None
        
        # PC菜单窗口
        self.pc_menu_window = None
        
        # 连接按钮和事件
        self.connect_ui_buttons()

    def select_file(self):
        """选择文件对话框"""
        file_name, _ = QtWidgets.QFileDialog.getOpenFileName(
            self,
            "选择页面序列文件",
            "",
            "文本文件 (*.txt);;所有文件 (*)",
            options=QtWidgets.QFileDialog.Options()
        )

        if file_name:
            try:
                with open(file_name, 'r', encoding='utf-8', errors='ignore') as f:
                    content = f.read()

                if hasattr(self.ui, 'pageLineEdit2_1'):  # 或换成你的控件名
                    self.ui.pageLineEdit2_1.setText(content)
            except Exception as e:
                QtWidgets.QMessageBox.warning(self, "错误", f"读取文件失败:\n{str(e)}")

    def setup_scroll_area(self):
        font = QtGui.QFont()
        font.setFamily("Consolas")
        font.setPointSize(10)
        self.ui.data_out_text.setFont(font)

        # 设置QTextEdit的样式，保持垂直和水平滚动条完全一致
        self.ui.data_out_text.setStyleSheet("""
            QTextEdit {
                background-color: white;
                border: 1px solid #ccc;
                padding: 10px;
            }
            
            /* 通用滚动条样式 - 确保垂直和水平滚动条一致 */
            QScrollBar:vertical, QScrollBar:horizontal {
                background-color: #f5f5f5;
                border-radius: 10px;
                border: 1px solid #e0e0e0;
            }
            
            /* 垂直滚动条 */
            QScrollBar:vertical {
                width: 16px;
                margin: 2px 2px 2px 2px;
            }
            
            /* 水平滚动条 */
            QScrollBar:horizontal {
                height: 16px;
                margin: 2px 2px 2px 2px;
            }
            
            /* 滚动条手柄样式 - 确保一致 */
            QScrollBar::handle:vertical, QScrollBar::handle:horizontal {
                background-color: #a0a0a0;
                border-radius: 8px;
                margin: 2px;
            }
            
            QScrollBar::handle:vertical {
                min-height: 25px;
            }
            
            QScrollBar::handle:horizontal {
                min-width: 25px;
            }
            
            /* 滚动条悬停效果 - 确保一致 */
            QScrollBar::handle:vertical:hover, QScrollBar::handle:horizontal:hover {
                background-color: #668fff;
            }
            
            /* 移除滚动条按钮 */
            QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical,
            QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
                height: 0px;
                width: 0px;
            }
            
            /* 移除滚动条上下角区域 */
            QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical,
            QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {
                background: none;
            }
            
            /* 角落区域 */
            QScrollBar::corner {
                background-color: #f5f5f5;
            }
        """)

        # 设置为只读模式
        self.ui.data_out_text.setReadOnly(True)

        # 禁用自动换行，保持表格格式
        self.ui.data_out_text.setLineWrapMode(QtWidgets.QTextEdit.NoWrap)

        # 设置文档边距
        self.ui.data_out_text.document().setDocumentMargin(10)

        # 确保QTextEdit有足够的大小来显示内容
        self.ui.data_out_text.setMinimumHeight(300)

        # 确保滚动条始终可见
        self.ui.data_out_text.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.ui.data_out_text.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)

    def connect_ui_buttons(self):
        """连接UI按钮信号与槽"""
        # 连接算法选择按钮
        self.ui.fifo.clicked.connect(self.click_fifo)
        self.ui.lru.clicked.connect(self.click_lru)
        self.ui.lfu.clicked.connect(self.click_lfu)
        self.ui.opt.clicked.connect(self.click_opt)
        
        # 连接模式选择按钮
        self.ui.random.clicked.connect(self.click_random)
        self.ui.hand.clicked.connect(self.click_hand)
        
        # 连接其他按钮
        self.ui.exit.clicked.connect(self.close)
        self.ui.minsize.clicked.connect(self.showMinimized)
        self.ui.icon.clicked.connect(self.select_file)

        # 连接模拟开始按钮
        self.ui.start_random.clicked.connect(self.handle_random_simulation)
        self.ui.start_hand.clicked.connect(self.handle_hand_simulation)
        
        # 连接性能分析按钮
        self.ui.performance.clicked.connect(self.show_performance_analysis)
        
        # 连接P-C模拟按钮
        self.ui.pcpage.clicked.connect(self.show_pc_menu)

    @pyqtSlot()
    def click_fifo(self):
        self.ui.fifo.setStyleSheet("QPushButton{background-color: #668fff; font-family: Calibri;"
                                   "border: none;font: bold;"
                                   "color: #000;border-radius: 7px;"
                                   "text-align: center;"
                                   "text-decoration: none;"
                                   "display: inline-block;"
                                   "font-size: 20px;"
                                   "cursor: pointer;"
                                   "}")

        self.ui.lru.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                  "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                  "text-align: center;text-decoration: none;display: inline-block;"
                                  "font-size: 20px;cursor: pointer;}"
                                  "QPushButton:hover{background-color: #7fa6c8; }")

        self.ui.lfu.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                  "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                  "text-align: center;text-decoration: none;display: inline-block;"
                                  "font-size: 20px;cursor: pointer;}"
                                  "QPushButton:hover{background-color: #7fa6c8; }")

        self.ui.opt.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                  "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                  "text-align: center;text-decoration: none;display: inline-block;"
                                  "font-size: 20px;cursor: pointer;}"
                                  "QPushButton:hover{background-color: #7fa6c8; }")

        self.select_algorithm = 1

    @pyqtSlot()
    def click_lru(self):
        self.ui.lru.setStyleSheet("QPushButton{background-color: #668fff; font-family: Calibri;"
                                  "border: none;font: bold;"
                                  "color: #000;border-radius: 7px;"
                                  "text-align: center;"
                                  "text-decoration: none;"
                                  "display: inline-block;"
                                  "font-size: 20px;"
                                  "cursor: pointer;"
                                  "}")

        self.ui.fifo.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                   "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                   "text-align: center;text-decoration: none;display: inline-block;"
                                   "font-size: 20px;cursor: pointer;}"
                                   "QPushButton:hover{background-color: #7fa6c8; }")

        self.ui.lfu.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                  "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                  "text-align: center;text-decoration: none;display: inline-block;"
                                  "font-size: 20px;cursor: pointer;}"
                                  "QPushButton:hover{background-color: #7fa6c8; }")

        self.ui.opt.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                  "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                  "text-align: center;text-decoration: none;display: inline-block;"
                                  "font-size: 20px;cursor: pointer;}"
                                  "QPushButton:hover{background-color: #7fa6c8; }")

        self.select_algorithm = 2

    @pyqtSlot()
    def click_lfu(self):
        self.ui.lfu.setStyleSheet("QPushButton{background-color: #668fff; font-family: Calibri;"
                                  "border: none;font: bold;"
                                  "color: #000;border-radius: 7px;"
                                  "text-align: center;"
                                  "text-decoration: none;"
                                  "display: inline-block;"
                                  "font-size: 20px;"
                                  "cursor: pointer;"
                                  "}")

        self.ui.lru.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                  "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                  "text-align: center;text-decoration: none;display: inline-block;"
                                  "font-size: 20px;cursor: pointer;}"
                                  "QPushButton:hover{background-color: #7fa6c8; }")

        self.ui.fifo.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                   "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                   "text-align: center;text-decoration: none;display: inline-block;"
                                   "font-size: 20px;cursor: pointer;}"
                                   "QPushButton:hover{background-color: #7fa6c8; }")

        self.ui.opt.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                  "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                  "text-align: center;text-decoration: none;display: inline-block;"
                                  "font-size: 20px;cursor: pointer;}"
                                  "QPushButton:hover{background-color: #7fa6c8; }")

        self.select_algorithm = 3

    @pyqtSlot()
    def click_opt(self):
        self.ui.opt.setStyleSheet("QPushButton{background-color: #668fff; font-family: Calibri;"
                                  "border: none;font: bold;"
                                  "color: #000;border-radius: 7px;"
                                  "text-align: center;"
                                  "text-decoration: none;"
                                  "display: inline-block;"
                                  "font-size: 20px;"
                                  "cursor: pointer;"
                                  "}")

        self.ui.lru.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                  "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                  "text-align: center;text-decoration: none;display: inline-block;"
                                  "font-size: 20px;cursor: pointer;}"
                                  "QPushButton:hover{background-color: #7fa6c8; }")

        self.ui.lfu.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                  "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                  "text-align: center;text-decoration: none;display: inline-block;"
                                  "font-size: 20px;cursor: pointer;}"
                                  "QPushButton:hover{background-color: #7fa6c8; }")

        self.ui.fifo.setStyleSheet("QPushButton{background-color: #ffffff; font-family: Calibri;"
                                   "font: bold;border-radius: 13px;color: #000;border-radius: 7px;"
                                   "text-align: center;text-decoration: none;display: inline-block;"
                                   "font-size: 20px;cursor: pointer;}"
                                   "QPushButton:hover{background-color: #7fa6c8; }")

        self.select_algorithm = 4

    @pyqtSlot()
    def click_random(self):
        # 设置样式
        self.ui.random.setStyleSheet("QPushButton{background-color: #eeeeee; font-family: 黑体;"
                                     "border: none;"
                                     "color: #000;border-radius: 7px;"
                                     "text-align: center;"
                                     "text-decoration: none;"
                                     "display: inline-block;"
                                     "font-size: 20px;"
                                     "cursor: pointer;"
                                     "}")

        self.ui.hand.setStyleSheet("QPushButton{background-color: #ffffff; font-family: 黑体;"
                                   "border-radius: 13px;color: #000;border-radius: 7px;"
                                   "text-align: center;text-decoration: none;display: inline-block;"
                                   "font-size: 20px;cursor: pointer;}"
                                   "QPushButton:hover{background-color: #7fa6c8; }")

        # 设置滑块动画
        random_rect = self.ui.random.geometry()
        frame_8_rect = self.ui.frame_8.geometry()
        red_frame_y = 101 - 3

        self.slide_animation.setStartValue(self.slider_indicator.geometry())
        self.slide_animation.setEndValue(QRect(
            random_rect.x() + frame_8_rect.x() - 2,
            red_frame_y,
            random_rect.width(),
            3
        ))
        self.slide_animation.start()

        self.select_way = 1

        self.ui.stackedWidget.setCurrentIndex(0)

    @pyqtSlot()
    def click_hand(self):
        # 设置样式
        self.ui.hand.setStyleSheet("QPushButton{background-color: #eeeeee; font-family: 黑体;"
                                   "border: none;"
                                   "color: #000;border-radius: 7px;"
                                   "text-align: center;"
                                   "text-decoration: none;"
                                   "display: inline-block;"
                                   "font-size: 20px;"
                                   "cursor: pointer;"
                                   "}")

        self.ui.random.setStyleSheet("QPushButton{background-color: #ffffff; font-family: 黑体;"
                                     "border-radius: 13px;color: #000;border-radius: 7px;"
                                     "text-align: center;text-decoration: none;display: inline-block;"
                                     "font-size: 20px;cursor: pointer;}"
                                     "QPushButton:hover{background-color: #7fa6c8; }")

        # 设置滑块动画
        hand_rect = self.ui.hand.geometry()
        frame_8_rect = self.ui.frame_8.geometry()
        red_frame_y = 101 - 3  # 与初始化位置相同

        self.slide_animation.setStartValue(self.slider_indicator.geometry())
        self.slide_animation.setEndValue(QRect(
            hand_rect.x() + frame_8_rect.x() - 2,
            red_frame_y,
            hand_rect.width(),
            3
        ))
        self.slide_animation.start()
        self.select_way = 2

        self.ui.stackedWidget.setCurrentIndex(1)

        # 实现窗口拖动

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self._drag_pos = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton and self._drag_pos:
            self.move(event.globalPos() - self._drag_pos)
            event.accept()

    def mouseReleaseEvent(self, event):
        self._drag_pos = None
        event.accept()

    @pyqtSlot()
    def handle_random_simulation(self):
        """处理随机生成模块的模拟开始按钮点击事件"""
        try:
            # 获取用户输入
            page_count_text = self.ui.pageLineEdit1_1.text().strip()
            page_range_text = self.ui.pageLineEdit1_2.text().strip()
            memory_size_text = self.ui.pageLineEdit1_3.text().strip()

            # 验证输入不为空
            if not page_count_text or not page_range_text or not memory_size_text:
                QtWidgets.QMessageBox.warning(self, "输入错误", "请填写所有输入项")
                return

            # 解析输入值
            try:
                page_count = int(page_count_text)
                page_range = page_range_text.split()
                if len(page_range) != 2:
                    raise ValueError("页面范围应包含两个数字")

                min_page = int(page_range[0])
                max_page = int(page_range[1])
                memory_size = int(memory_size_text)

                # 验证输入合法性
                if page_count <= 0:
                    raise ValueError("页面序列长度必须大于0")
                if min_page >= max_page:
                    raise ValueError("页面范围的最小值必须小于最大值")
                if memory_size <= 0:
                    raise ValueError("内存块数必须大于0")

            except ValueError as e:
                QtWidgets.QMessageBox.warning(self, "输入错误", str(e))
                return

            # 生成随机页面序列
            page_sequence = [random.randint(min_page, max_page) for _ in range(page_count)]

            # 先清空QTextEdit
            self.ui.data_out_text.clear()

            # 根据选择的算法执行相应的处理
            if self.select_algorithm == 1:
                main_instance = FIFOMain(page_sequence, memory_size)
                result = self.format_result_with_colored_faults(main_instance)
                self.ui.data_out_text.setHtml(result)
                self.setcilv(main_instance.get_lv(), main_instance.get_ci(), main_instance.get_que())
            elif self.select_algorithm == 2:
                main_instance = LRUMain(page_sequence, memory_size)
                result = self.format_result_with_colored_faults(main_instance)
                self.ui.data_out_text.setHtml(result)
                self.setcilv(main_instance.get_lv(), main_instance.get_ci(), main_instance.get_que())
            elif self.select_algorithm == 3:
                main_instance = LFUMain(page_sequence, memory_size)
                result = self.format_result_with_colored_faults(main_instance)
                self.ui.data_out_text.setHtml(result)
                self.setcilv(main_instance.get_lv(), main_instance.get_ci(), main_instance.get_que())
            elif self.select_algorithm == 4:
                # 修正OPT算法调用，参数顺序应为 memory_size, page_sequence
                algorithm = OPT(memory_size, page_sequence)
                result = self.format_result_with_colored_faults(algorithm)
                self.ui.data_out_text.setHtml(result)
                self.setcilv(algorithm.get_lv(), algorithm.get_ci(), algorithm.get_que())

            # 确保滚动条重置到顶部
            self.ui.data_out_text.verticalScrollBar().setValue(0)
            self.ui.data_out_text.horizontalScrollBar().setValue(0)
            self.ui.data_out_text.moveCursor(QtGui.QTextCursor.Start)

            # 强制更新UI
            self.ui.data_out_text.repaint()
            QtWidgets.QApplication.processEvents()

        except Exception as e:
            QtWidgets.QMessageBox.critical(self, "错误", f"模拟过程中发生错误：{str(e)}")

    def handle_hand_simulation(self):
        """处理手动输入页面的模拟开始按钮点击事件"""
        try:
            # 获取用户输入
            page_sequence_text = self.ui.pageLineEdit2_1.text().strip()
            memory_size_text = self.ui.pageLineEdit2_2.text().strip()

            # 验证输入不为空
            if not page_sequence_text or not memory_size_text:
                QtWidgets.QMessageBox.warning(self, "输入错误", "请填写所有输入项")
                return

            # 解析输入值
            try:
                # 解析页面序列 - 支持空格、逗号或分号分隔
                page_sequence = []
                for item in page_sequence_text.replace(',', ' ').replace(';', ' ').split():
                    page_sequence.append(int(item))

                memory_size = int(memory_size_text)

                # 验证输入合法性
                if not page_sequence:
                    raise ValueError("页面序列不能为空")
                if memory_size <= 0:
                    raise ValueError("内存块数必须大于0")

            except ValueError as e:
                QtWidgets.QMessageBox.warning(self, "输入错误", str(e))
                return

            # 先清空QTextEdit
            self.ui.data_out_text.clear()

            # 根据选择的算法执行相应的处理
            if self.select_algorithm == 1:
                main_instance = FIFOMain(page_sequence, memory_size)
                result = self.format_result_with_colored_faults(main_instance)
                self.ui.data_out_text.setHtml(result)
                self.setcilv(main_instance.get_lv(), main_instance.get_ci(), main_instance.get_que())
            elif self.select_algorithm == 2:
                main_instance = LRUMain(page_sequence, memory_size)
                result = self.format_result_with_colored_faults(main_instance)
                self.ui.data_out_text.setHtml(result)
                self.setcilv(main_instance.get_lv(), main_instance.get_ci(), main_instance.get_que())
            elif self.select_algorithm == 3:
                main_instance = LFUMain(page_sequence, memory_size)
                result = self.format_result_with_colored_faults(main_instance)
                self.ui.data_out_text.setHtml(result)
                self.setcilv(main_instance.get_lv(), main_instance.get_ci(), main_instance.get_que())
            elif self.select_algorithm == 4:
                # 修正OPT算法调用，参数顺序应为 memory_size, page_sequence
                algorithm = OPT(memory_size, page_sequence)
                result = self.format_result_with_colored_faults(algorithm)
                self.ui.data_out_text.setHtml(result)
                self.setcilv(algorithm.get_lv(), algorithm.get_ci(), algorithm.get_que())

            # 确保滚动条重置到顶部
            self.ui.data_out_text.verticalScrollBar().setValue(0)
            self.ui.data_out_text.horizontalScrollBar().setValue(0)
            self.ui.data_out_text.moveCursor(QtGui.QTextCursor.Start)

            # 强制更新UI
            self.ui.data_out_text.repaint()
            QtWidgets.QApplication.processEvents()

        except Exception as e:
            QtWidgets.QMessageBox.critical(self, "错误", f"模拟过程中发生错误：{str(e)}")

    def format_result_with_colored_faults(self, algorithm_instance):
        """
        格式化算法结果，将缺页索引显示为红色
        :param algorithm_instance: 算法实例
        :return: 带有HTML格式的结果字符串
        """
        # 获取原始结果
        result = algorithm_instance.get_res()

        # 获取缺页队列
        # strip: '1 2 3 6 9 8 7 4 5 6 3 2 5 8 9' 去除多余空格
        # strip().split(): ['1', '2', '3', '6', '9', '8', '7', '4', '5', '6', '3', '2', '5', '8', '9'] 默认以空格分开列表
        fault_queue = algorithm_instance.get_que().strip().split()

        # 将结果转换为HTML格式，并用红色标记缺页索引
        html_result = '<pre>'  # 使用pre标签保留格式

        lines = result.split('\n')
        # 获取页面序列
        if len(lines) > 1:
            # '|    1 |    2 |    3 |    6 |    9 |    8 |    7 |    4 |    5 |    6 |    3 |    2 |    5 |    8 |    9 |'
            header_line = lines[1]
            # [1, 2, 3, 6, 9, 8, 7, 4, 5, 6, 3, 2, 5, 8, 9]
            page_sequence = []
            for segment in header_line.split('|'):
                if segment.strip():
                    try:
                        page_num = int(segment.strip())
                        page_sequence.append(page_num)
                    except ValueError:
                        pass

        # 创建缺页位置索引集合
        fault_positions = set()
        fault_index = 0

        # 遍历页面序列，标记缺页位置  fault_queue为缺页队列 只要新页面与当前缺页队列对应的索引匹配就说明缺页 否则就不是缺页
        for i, page in enumerate(page_sequence):
            # 只有匹配才会将fault_queue为缺页队列的索引加一 否则只加page_sequence 因为缺页是按顺序发生的
            if fault_index < len(fault_queue) and str(page) == fault_queue[fault_index]:
                fault_positions.add(i)
                fault_index += 1

        # 生成HTML格式的输出
        for i, line in enumerate(lines):
            if i == 0 or i == 2 or i == len(lines) - 1:
                # 表格边框行，保持原样
                html_result += line + '\n'
            elif i == 1:
                # 页面序列行，只为缺页位置的页面添加红色
                segments = line.split('|')
                html_line = ''
                segment_index = 0

                for j, segment in enumerate(segments):
                    if segment.strip():
                        try:
                            page_num = int(segment.strip())
                            if segment_index in fault_positions:
                                # 缺页的页面用红色标记
                                html_line += '|<span style="color: red; font-weight: bold;">' + segment + '</span>'
                            else:
                                html_line += '|' + segment
                            segment_index += 1
                        except ValueError:
                            html_line += '|' + segment
                    elif j == 0:
                        html_line += segment
                    elif j == len(segments) - 1:
                        html_line += '|' + segment
                html_result += html_line + '\n'
            else:
                # 内存状态行，保持原样
                html_result += line + '\n'

        html_result += '</pre>'
        return html_result

    @pyqtSlot()
    def setcilv(self, lv, ci, que):
        self.ui.statisticLineEdit1.setText(str(ci) + "次")
        self.ui.statisticLineEdit2.setText(str("%.2f%%" % (lv * 100)))  # 只保留两位小数
        self.ui.statisticLineEdit3.setText(str(que))
        # 缺页率 *（2 * 单位访存时间 + 缺页中断时间）+（1 - 缺页率） * （2 * 单位访存时间）
        delay = lv * (2 * 0.1 + 10) + (1 - lv) * (2 * 0.1)
        text = "%.2fms" % delay
        self.ui.statisticLineEdit4.setText(text)
        # 根据缺页率设置不同的颜色
        if lv > 0.75:
            self.ui.statisticLineEdit1.setStyleSheet("QLineEdit{background-color: #e89291; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit2.setStyleSheet("QLineEdit{background-color: #e89291; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit3.setStyleSheet("QLineEdit{background-color: #e89291; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit4.setStyleSheet("QLineEdit{background-color: #e89291; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
        elif lv > 0.50:
            self.ui.statisticLineEdit1.setStyleSheet("QLineEdit{background-color: #f1916b; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit2.setStyleSheet("QLineEdit{background-color: #f1916b; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit3.setStyleSheet("QLineEdit{background-color: #f1916b; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit4.setStyleSheet("QLineEdit{background-color: #f1916b; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
        elif lv > 0.25:
            self.ui.statisticLineEdit1.setStyleSheet("QLineEdit{background-color: #8cc9c4; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit2.setStyleSheet("QLineEdit{background-color: #8cc9c4; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit3.setStyleSheet("QLineEdit{background-color: #8cc9c4; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit4.setStyleSheet("QLineEdit{background-color: #8cc9c4; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
        else:
            self.ui.statisticLineEdit1.setStyleSheet("QLineEdit{background-color:#83bde2; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit2.setStyleSheet("QLineEdit{background-color: #83bde2; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit3.setStyleSheet("QLineEdit{background-color: #83bde2; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
            self.ui.statisticLineEdit4.setStyleSheet("QLineEdit{background-color: #83bde2; font-family: 微软雅黑;" +
                                                     "border: none; " +
                                                     "color: #fff;border-radius: 5px;" +
                                                     "text-align: center;" +
                                                     "text-decoration: none;" +
                                                     "display: inline-block;" +
                                                     "font-size: 19px;}")
        # 为统计行文本框设置阴影效果
        shadow1 = QGraphicsDropShadowEffect(self)
        shadow1.setBlurRadius(15)
        shadow1.setColor(QColor("#cacaca"))
        shadow1.setOffset(1, 1)
        self.ui.statisticLineEdit1.setGraphicsEffect(shadow1)

        shadow2 = QGraphicsDropShadowEffect(self)
        shadow2.setBlurRadius(15)
        shadow2.setColor(QColor("#cacaca"))
        shadow2.setOffset(1, 1)
        self.ui.statisticLineEdit2.setGraphicsEffect(shadow2)

        shadow3 = QGraphicsDropShadowEffect(self)
        shadow3.setBlurRadius(15)
        shadow3.setColor(QColor("#cacaca"))
        shadow3.setOffset(1, 1)
        self.ui.statisticLineEdit3.setGraphicsEffect(shadow3)

    def show_performance_analysis(self):
        if self.analysis_window is None:
            self.analysis_window = AlgorithmAnalysisWindow()
        # 获取主窗口 性能分析窗口的大小
        main_geometry = self.geometry()
        analysis_geometry = self.analysis_window.geometry()
        # 计算中心位置 向下取整 //
        x = main_geometry.x() + (main_geometry.width() - analysis_geometry.width()) // 2
        y = main_geometry.y() + (main_geometry.height() - analysis_geometry.height()) // 2
        self.analysis_window.move(x, y)
        self.analysis_window.show()
        # 如果该窗口被其他窗口覆盖，调用此方法后，它将被带到前面并获得焦点
        self.analysis_window.activateWindow()

    def show_pc_menu(self):
        if not hasattr(self, 'pc_menu_window') or self.pc_menu_window is None:
            self.pc_menu_window = PCMenuWindow()
        # 获取主窗口和PC菜单窗口的大小
        main_geometry = self.geometry()
        pc_menu_geometry = self.pc_menu_window.geometry()
        # 计算中心位置
        x = main_geometry.x() + (main_geometry.width() - pc_menu_geometry.width()) // 2
        y = main_geometry.y() + (main_geometry.height() - pc_menu_geometry.height()) // 2
        self.pc_menu_window.move(x, y)
        self.pc_menu_window.show()
        # 如果窗口被其他窗口覆盖，调用此方法后，它将被带到前面并获得焦点
        self.pc_menu_window.activateWindow()

class AlgorithmAnalysisWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(AlgorithmAnalysisWindow, self).__init__()
        self.canvas2 = None
        self.figure2 = None
        self.canvas1 = None
        self.figure1 = None
        self.ui = Ui_Algorithm_Analysis()
        self.ui.setupUi(self)

        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("./icons/page.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.setWindowIcon(icon)

        # 设置窗口无边框
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)

        # 添加拖动相关变量
        self._drag_pos = None

        # 连接信号和槽
        self.ui.exit.clicked.connect(self.close)
        self.ui.minsize.clicked.connect(self.showMinimized)
        # 点击性能分析就要重新刷新一次图表  plt.close('all')
        self.ui.start_.clicked.connect(self.run_performance_analysis)
        # 初始化matplotlib图表
        self.setup_plots()

    def setup_plots(self):
        # 创建第一个性能比较图表（页面长度与缺页率的关系）
        # 指定图形大小与分辨率
        self.figure1 = Figure(figsize=(10, 4), dpi=100)
        # 将 Figure 对象嵌入到 PyQt 的窗口中
        self.canvas1 = FigureCanvas(self.figure1)
        layout1 = QVBoxLayout()
        layout1.addWidget(self.canvas1)
        self.ui.plot1.setLayout(layout1)

        # 创建第二个性能比较图表（内存块数与缺页率的关系）
        self.figure2 = Figure(figsize=(10, 4), dpi=100)
        self.canvas2 = FigureCanvas(self.figure2)
        layout2 = QVBoxLayout()
        layout2.addWidget(self.canvas2) 
        self.ui.plot2.setLayout(layout2)

        # 初始化第一个图表
        ax1 = self.figure1.add_subplot(111)
        ax1.set_xlabel('页面序列长度')
        ax1.set_ylabel('缺页率 (%)')
        ax1.set_title('不同页面序列长度下各算法缺页率比较')
        ax1.yaxis.grid(True, linestyle='--', alpha=0.7)
        # 设置 X 轴和 Y 轴的范围 自动计算合适的刻度步长
        ax1.set_ylim(0, 100)
        ax1.set_xlim(0, 10)
        
        # 初始化第二个图表
        ax2 = self.figure2.add_subplot(111)
        ax2.set_xlabel('内存块数')
        ax2.set_ylabel('缺页率 (%)')
        ax2.set_title('不同内存块数下各算法缺页率比较')
        ax2.yaxis.grid(True, linestyle='--', alpha=0.7)
        ax2.set_ylim(0, 100)
        ax2.set_xlim(0, 10)

        # 用来正常显示中文标签
        plt.rcParams['font.sans-serif'] = ['SimHei']
        # 用来正常显示负号
        plt.rcParams['axes.unicode_minus'] = False

        # 优化图形的布局，确保所有元素清晰可见
        self.figure1.tight_layout()
        self.figure2.tight_layout()
        # 更新图形的显示，确保用户界面反映最新的图形状态
        self.canvas1.draw()
        self.canvas2.draw()

    def run_performance_analysis(self):
        try:
            max_page_text = self.ui.maxpage.text().strip()
            page_range_text = self.ui.pagerange.text().strip()
            storage_text = self.ui.storage.text().strip()

            if not max_page_text or not page_range_text or not storage_text:
                QtWidgets.QMessageBox.warning(self, "输入错误", "请填写所有输入项")
                return

            try:
                max_page_count = int(max_page_text)
                page_range = page_range_text.split()
                if len(page_range) != 2:
                    raise ValueError("页面范围应包含两个数字")

                min_page = int(page_range[0])
                max_page = int(page_range[1])
                memory_size = int(storage_text)

                if max_page_count <= 0:
                    raise ValueError("页面最大值必须大于0")
                if min_page >= max_page:
                    raise ValueError("页面范围的最小值必须小于最大值")
                if memory_size <= 0:
                    raise ValueError("内存块数必须大于0")

            except ValueError as e:
                QtWidgets.QMessageBox.warning(self, "输入错误", str(e))
                return

            # 完全重置图表状态 - 重新创建图形和画布   关闭所有图形
            plt.close('all')

            # 重新创建图形和画布
            self.figure1 = Figure(figsize=(10, 4), dpi=100)
            self.canvas1 = FigureCanvas(self.figure1)
            layout1 = self.ui.plot1.layout()
            if layout1 is not None:
                while layout1.count():
                    item = layout1.takeAt(0)
                    widget = item.widget()
                    if widget is not None:
                        # 清除之前的图形和画布
                        widget.deleteLater()
            else:
                layout1 = QVBoxLayout(self.ui.plot1)
            layout1.addWidget(self.canvas1)

            self.figure2 = Figure(figsize=(10, 4), dpi=100)
            self.canvas2 = FigureCanvas(self.figure2)
            layout2 = self.ui.plot2.layout()
            if layout2 is not None:
                while layout2.count():
                    item = layout2.takeAt(0)
                    widget = item.widget()
                    if widget is not None:
                        widget.deleteLater()
            else:
                layout2 = QVBoxLayout(self.ui.plot2)
            layout2.addWidget(self.canvas2)
            
            # 初始化两个图表，显示空的表格框架
            # 初始化第一个图表
            ax1 = self.figure1.add_subplot(111)
            ax1.set_xlabel('页面序列长度')
            ax1.set_ylabel('缺页率 (%)')
            ax1.set_title(f'不同页面序列长度下各算法缺页率比较 (内存块数={memory_size}, 页面序列范围={min_page}-{max_page})')
            ax1.yaxis.grid(True, linestyle='--', alpha=0.7)
            ax1.set_ylim(0, 105)
            ax1.set_xlim(0, max_page_count * 1.1)
            
            # 初始化第二个图表
            ax2 = self.figure2.add_subplot(111)
            ax2.set_xlabel('内存块数')
            ax2.set_ylabel('缺页率 (%)')
            ax2.set_title(f'不同内存块数下各算法缺页率比较 (页面序列长度={max_page_count}, 页面序列范围={min_page}-{max_page})')
            ax2.yaxis.grid(True, linestyle='--', alpha=0.7)
            ax2.set_ylim(0, 105)
            ax2.set_xlim(0, memory_size * 1.1)
            
            # 添加图例
            ax1.legend(['FIFO算法', 'LRU算法', 'LFU算法', 'OPT算法'], loc='best')
            ax2.legend(['FIFO算法', 'LRU算法', 'LFU算法', 'OPT算法'], loc='best')
            
            # 立即显示初始图表
            self.figure1.tight_layout()
            self.figure2.tight_layout()
            self.canvas1.draw()
            self.canvas2.draw()

            # 在执行绘图的耗时操作时，改变鼠标光标的样式为沙漏
            QtWidgets.QApplication.setOverrideCursor(Qt.WaitCursor)

            # 生成页面序列 用于对不同内存块数进行分析时指定的页面序列
            page_sequence = [random.randint(min_page, max_page) for _ in range(max_page_count)]

            # 图表的横轴数据
            sequence_lengths = np.arange(1, max_page_count + 1, 1, dtype=int)
            memory_sizes = np.arange(1, memory_size + 1, 1, dtype=int)
            
            # 初始化结果列表
            fifo_rates_seq = []
            lru_rates_seq = []
            lfu_rates_seq = []
            opt_rates_seq = []
            
            fifo_rates_mem = []
            lru_rates_mem = []
            lfu_rates_mem = []
            opt_rates_mem = []

            # 获取最大迭代次数
            max_iterations = max(len(sequence_lengths), len(memory_sizes))
            
            # 同时分析两种情况 对每个迭代次数分别作为生成序列的长度与内存块数的索引
            for i in range(max_iterations):
                # 分析1：页面序列长度影响 每次生成变化的 Seq 而 memory_size 保持不变
                if i < len(sequence_lengths):
                    length = sequence_lengths[i]
                    seq = [random.randint(min_page, max_page) for _ in range(length)]
                    
                    # FIFO算法 返回缺页次数, 缺页率, 详细步骤
                    fifo = FIFO(seq, memory_size)
                    _, fault_rate, _ = fifo.run()
                    fifo_rates_seq.append(fault_rate)
                    
                    # LRU算法
                    lru = LRU(seq, memory_size)
                    _, fault_rate, _ = lru.run()
                    lru_rates_seq.append(fault_rate)
                    
                    # LFU算法
                    lfu = LFU(seq, memory_size)
                    _, fault_rate, _ = lfu.run()
                    lfu_rates_seq.append(fault_rate)
                    
                    # OPT算法
                    opt = OPT(memory_size, seq)
                    fault_rate = opt.get_lv() * 100
                    opt_rates_seq.append(fault_rate)
                    
                    # 获取当前活动的坐标轴对象
                    ax1 = self.figure1.gca()
                    ax1.clear()
                    ax1.set_xlabel('页面序列长度')
                    ax1.set_ylabel('缺页率 (%)')
                    ax1.set_title(f'不同页面序列长度下各算法缺页率比较 (内存块数={memory_size}, 页面序列范围={min_page}-{max_page})')
                    ax1.yaxis.grid(True, linestyle='--', alpha=0.7)
                    
                    current_lengths = sequence_lengths[:i+1]
                    ax1.plot(current_lengths, fifo_rates_seq, '-', color='red', label='FIFO算法', linewidth=2.5)
                    ax1.plot(current_lengths, lru_rates_seq, '--', color='blue', label='LRU算法', linewidth=2.5)
                    ax1.plot(current_lengths, lfu_rates_seq, ':', color='lime', label='LFU算法', linewidth=2.5)
                    ax1.plot(current_lengths, opt_rates_seq, '-.', color='magenta', label='OPT算法', linewidth=2.5)
                    
                    ax1.set_xlim(0, max_page_count * 1.1)
                    ax1.set_ylim(0, 105)
                    ax1.legend(loc='best')
                    self.figure1.tight_layout()
                    self.canvas1.draw()

                # 分析2：内存块数影响
                if i < len(memory_sizes):
                    size = memory_sizes[i]

                    # 每次生成变化的内存 size 而 page_sequence 保持不变
                    # FIFO算法
                    fifo = FIFO(page_sequence, size)
                    _, fault_rate, _ = fifo.run()
                    fifo_rates_mem.append(fault_rate)
                    
                    # LRU算法
                    lru = LRU(page_sequence, size)
                    _, fault_rate, _ = lru.run()
                    lru_rates_mem.append(fault_rate)
                    
                    # LFU算法
                    lfu = LFU(page_sequence, size)
                    _, fault_rate, _ = lfu.run()
                    lfu_rates_mem.append(fault_rate)
                    
                    # OPT算法
                    opt = OPT(size, page_sequence)
                    fault_rate = opt.get_lv() * 100
                    opt_rates_mem.append(fault_rate)
                    
                    # 更新第二张图
                    ax2 = self.figure2.gca()
                    ax2.clear()
                    ax2.set_xlabel('内存块数')
                    ax2.set_ylabel('缺页率 (%)')
                    ax2.set_title(f'不同内存块数下各算法缺页率比较 (页面序列长度={max_page_count}, 页面序列范围={min_page}-{max_page})')
                    ax2.yaxis.grid(True, linestyle='--', alpha=0.7)
                    
                    current_sizes = memory_sizes[:i+1]
                    ax2.plot(current_sizes, fifo_rates_mem, '-', color='red', label='FIFO算法', linewidth=2.5)
                    ax2.plot(current_sizes, lru_rates_mem, '--', color='blue', label='LRU算法', linewidth=2.5)
                    ax2.plot(current_sizes, lfu_rates_mem, ':', color='lime', label='LFU算法', linewidth=2.5)
                    ax2.plot(current_sizes, opt_rates_mem, '-.', color='magenta', label='OPT算法', linewidth=2.5)
                    
                    ax2.set_xlim(0, memory_size * 1.1)
                    ax2.set_ylim(0, 105)
                    ax2.legend(loc='best')
                    self.figure2.tight_layout()
                    self.canvas2.draw()
                
                # 处理事件，保持界面响应
                QtWidgets.QApplication.processEvents()

            # 从漏斗中恢复正常光标
            QtWidgets.QApplication.restoreOverrideCursor()

        except Exception as e:
            # 恢复光标
            QtWidgets.QApplication.restoreOverrideCursor()
            QtWidgets.QMessageBox.critical(self, "错误", f"性能分析过程中发生错误：{str(e)}")

    # 实现窗口拖动
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self._drag_pos = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton and self._drag_pos:
            self.move(event.globalPos() - self._drag_pos)
            event.accept()

    def mouseReleaseEvent(self, event):
        self._drag_pos = None
        event.accept()

class PCMenuWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(PCMenuWindow, self).__init__()
        self.ui = Ui_PCMenu()
        self.ui.setupUi(self)
        self.setWindowTitle("模拟生产-消费者请求页面调度可视化系统")
        
        # 设置窗口图标
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("./icons/page.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.setWindowIcon(icon)
        
        # 设置无边框窗口
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        
        # 拖动相关变量
        self._drag_pos = None
        
        # 初始化参数
        self.consumer_count = 1  # 默认1个消费者，不可修改
        self.page_offset_bits = 4  # 页内地址为4位，不可修改
        self.logical_address_size = 1  # 页面逻辑地址，默认1KB
        self.memory_blocks = 8  # 内存块数，默认为8
        self.buffer_size = 4  # 缓冲区存放页面数量，默认为4
        self.producer_count = 4  # 生产者数量，默认为4
        self.replacement_algorithm = "FIFO"  # 页面置换算法，默认FIFO
        self.simulation_speed = 500  # 模拟速度，默认500ms
        
        # 生产者速度倍数，默认为1.0倍
        self.producer_speed_multiplier = 1.0
        
        # 生产模式
        self.production_mode = "Random"  # 默认为随机生成
        self.random_mode = True  # 是否为随机模式，与production_mode同步
        
        # 存储从文件读取的页面序列
        self.file_page_sequence = []
        self.file_page_index = 0
        
        # 初始化系统参数
        # self.access_time = 100  # 访存时间，100ns
        # self.page_fault_time = 10000000  # 缺页中断处理时间，10ms = 10,000,000ns
        self.hit_count = 0  # 命中次数
        self.miss_count = 0  # 缺页次数
        self.total_count = 0  # 总访问次数
        self.replace_count = 0  # 置换次数
        self.tlb_hit_count = 0  # 快表命中次数
        
        # 生产者状态 - 确保初始化为producer_count长度的列表
        self.producers_active = [False] * self.producer_count  # 生产者状态，True表示活跃
        self.producer_data = []  # 生产者生成的页面数据
        
        # 为每个生产者设置不同的初始速度
        self.producer_speeds = []
        # 保存原始速度，用于倍数调整
        self.original_producer_speeds = []
        for i in range(self.producer_count):
            # 使用设定的模拟速度作为基础，添加一些随机变化
            base_speed = self.simulation_speed
            variation = random.uniform(0.8, 1.2)  # 速度变化在基础速度的80%到120%之间
            original_speed = int(base_speed * variation)
            self.original_producer_speeds.append(original_speed)  # 保存原始速度
            
            # 应用倍数计算实际速度
            actual_speed = int(original_speed * self.producer_speed_multiplier)
            self.producer_speeds.append(actual_speed)
        
        # 生产者队列 - 存储每个生产者待处理的页面
        self.producer_queues = [[] for _ in range(self.producer_count)]
        self.producer_current_pages = [None] * self.producer_count  # 当前显示的页面
        
        # 消费者状态
        self.consumer_active = False
        
        # 内存和缓冲区
        self.memory = []  # 内存中的页面
        self.buffer = []  # 缓冲区中的页面
        self.tlb = []  # 快表
        self.tlb_size = 4  # 快表大小，默认为4
        
        # 页面引用记录（用于LRU和LFU算法）
        self.page_reference_counts = {}  # 页面引用次数
        self.page_last_used = {}  # 页面最后使用时间
        
        # 初始化页表和FIFO队列
        self.page_table = {}
        self.fifo_queue = []
        
        # 设置UI控件的初始值
        self.ui.pageAddress.setValue(1)  # 默认页面大小为1KB
        self.ui.pageAddress.valueChanged.connect(self.update_address_size)
        
        self.ui.memoryBlockNumber.setValue(self.memory_blocks)
        self.ui.memoryBlockNumber.valueChanged.connect(self.update_memory_blocks)
        
        self.ui.bufferPage.setValue(self.buffer_size)
        self.ui.bufferPage.valueChanged.connect(self.update_buffer_size)
        
        self.ui.producer.setValue(self.producer_count)
        self.ui.producer.valueChanged.connect(self.update_producer_count)
        
        # 设置生产者速度倍数滑块 - 控制生产者生产速度的倍数
        self.ui.horizontalSlider.setMinimum(50)  # 0.5倍
        self.ui.horizontalSlider.setMaximum(200)  # 2.0倍
        self.ui.horizontalSlider.setValue(100)  # 默认1.0倍
        self.ui.horizontalSlider.valueChanged.connect(self.update_producer_speed_multiplier)
        
        # 设置页面调入速度滑块 - 控制页面从生产者到缓冲区的动画速度
        self.ui.horizontalSlider_2.setMinimum(100)
        self.ui.horizontalSlider_2.setMaximum(1000)
        self.ui.horizontalSlider_2.setValue(500)  # 默认中等速度
        self.ui.horizontalSlider_2.setEnabled(True)  # 启用，允许用户调整
        self.ui.horizontalSlider_2.valueChanged.connect(self.update_animation_speed)
        
        # 设置页面动画速度
        self.page_animation_duration = 5000  # 默认5秒
        # 初始化动画速度
        self.update_animation_speed(self.ui.horizontalSlider_2.value())

        # 设置算法选择
        self.ui.algorithm.currentIndexChanged.connect(self.update_replacement_algorithm)

        self.ui.module.setStyleSheet("""
            QComboBox {
                font-family: Consolas;
                font-size: 16px;
                selection-background-color: #668fff;
            }
            QComboBox::drop-down {
                width: 25px;
                border-left: 1px solid #c0c0c0;
            }
            QComboBox QAbstractItemView {
                font-family: Consolas;
                font-size: 16px;
                selection-background-color: #668fff;
            }
        """)

        self.ui.algorithm.setStyleSheet("""
            QComboBox {
                font-family: Consolas;
                font-size: 16px;
                selection-background-color: #668fff;
            }
            QComboBox::drop-down {
                width: 25px;
                border-left: 1px solid #c0c0c0;
            }
            QComboBox QAbstractItemView {
                font-family: Consolas;
                font-size: 16px;
                selection-background-color: #668fff;
            }
        """)

        for i in range(self.ui.module.count()):
            text = self.ui.module.itemText(i)
            self.ui.module.setItemText(i, "     " + text + "     ")
            
        for i in range(self.ui.algorithm.count()):
            text = self.ui.algorithm.itemText(i)
            self.ui.algorithm.setItemText(i, "     " + text + "     ")

        self.ui.module.currentIndexChanged.connect(self.update_production_mode)

        # 连接按钮信号
        self.ui.pushButton.clicked.connect(self.start_simulation)
        self.ui.reset_button.clicked.connect(self.reset_simulation)
        self.ui.exit.clicked.connect(self.close)
        self.ui.minsize.clicked.connect(self.showMinimized)
        self.ui.icon.clicked.connect(self.select_file)
        
        # 设置生产者和消费者控制按钮
        self.ui.producer_2.clicked.connect(self.toggle_producer)
        self.ui.consumer.clicked.connect(self.toggle_consumer)
        
        # 初始状态下禁用生产者和消费者按钮，直到点击开始模拟
        self.ui.producer_2.setEnabled(False)
        self.ui.consumer.setEnabled(False)
        
        # 初始化模拟状态标志
        self.simulation_started = False
        
        # 隐藏文件路径输入框（初始为随机模式）
        self.update_file_path_visibility()
        
        # 更新滑块上方的文字显示
        self.update_labels()
        
        # 初始化图形场景
        self.scene = QGraphicsScene()
        self.ui.graphicsView.setScene(self.scene)
        
        # 初始化定时器
        self.producer_timers = []
        for i in range(self.producer_count):
            # 为每个生产者创建独立的定时器
            timer = QTimer(self)  # 确保定时器有父对象
            # 使用functools.partial确保每个定时器调用正确的生产者索引
            import functools
            timer.timeout.connect(functools.partial(self.produce_page_for_producer, i))
            timer.setInterval(self.producer_speeds[i])
            self.producer_timers.append(timer)
        
        self.consumer_timer = QTimer(self)
        self.consumer_timer.timeout.connect(self.consume_page)
        self.consumer_timer.setInterval(self.simulation_speed)  # 消费者稍慢于生产者
        
        # 初始化临时动画项目列表
        self.temp_animation_items = []
        
        # 设置场景的鼠标点击事件
        self.ui.graphicsView.mousePressEvent = self.scene_mouse_press_event
        
        # 添加初始提示文本
        self.start_hint = QtWidgets.QGraphicsTextItem("请点击左侧的开始模拟按钮进行操作")
        self.start_hint.setFont(QtGui.QFont("Arial", 25, QtGui.QFont.Bold))
        self.start_hint.setDefaultTextColor(QtCore.Qt.darkBlue)
        self.start_hint.setPos(300, 200)
        self.scene.addItem(self.start_hint)

    def update_address_size(self, value):
        bits = int(math.log2(value * 1024))
        
        # 确保逻辑地址空间大小大于页内偏移位数
        if bits <= self.page_offset_bits:
            # 显示警告消息
            self.show_status_message(
                f"警告: 逻辑地址空间({bits}位)必须大于页内偏移({self.page_offset_bits}位)！", 
                "red", 
                5000
            )
            # 自动调整为页内偏移位数+4
            bits = self.page_offset_bits + 4
            # 反向计算对应的KB值
            kb_value = int(2**(bits) / 1024)
            
            # 如果有值设置器，更新其值
            if hasattr(self.ui, 'pageAddress'):
                self.ui.pageAddress.setValue(kb_value)
                
            # 更新逻辑地址空间大小（存储KB值）
            self.logical_address_size = kb_value
            self.update_labels()
        else:
            # 正常更新KB值
            self.logical_address_size = value
            self.update_labels()
    
    def update_memory_blocks(self, value):
        self.memory_blocks = value
        self.update_labels()
    
    def update_buffer_size(self, value):
        self.buffer_size = value
        self.update_labels()
    
    def update_producer_count(self, value):
        old_count = self.producer_count
        self.producer_count = value
        
        # 调整生产者状态数组大小
        if value > old_count:
            # 增加生产者
            self.producers_active.extend([False] * (value - old_count))
        else:
            # 减少生产者
            self.producers_active = self.producers_active[:value]
        
        self.update_labels()
    
    def update_replacement_algorithm(self, index):
        self.replacement_algorithm = self.ui.algorithm.itemText(index).strip()
        self.update_labels()
    
    def update_production_mode(self, index):
        mode_text = self.ui.module.itemText(index)
        if mode_text == "     Random     ":
            self.production_mode = "Random"
            self.random_mode = True
        elif mode_text == "     File     ":
            self.production_mode = "File"
            self.random_mode = False
        else:
            # 默认为随机模式
            self.production_mode = "Random"
            self.random_mode = True

        self.update_file_path_visibility()
    
    def update_file_path_visibility(self):
        if self.production_mode == "File":
            self.ui.path.setVisible(True)
            self.ui.icon.setVisible(True)

        else:
            self.ui.path.setVisible(False)
            self.ui.icon.setVisible(False)
    
    def select_file(self):
        """选择文件对话框"""
        file_name, _ = QtWidgets.QFileDialog.getOpenFileName(
            self,
            "选择页面序列文件",
            "",
            "文本文件 (*.txt);;所有文件 (*)",
            options=QtWidgets.QFileDialog.Options()
        )
        
        if file_name:
            self.file_path = file_name
            # 显示文件名（不是完整路径）
            file_display_name = os.path.basename(file_name)
            
            # 根据UI组件显示文件名
            if hasattr(self.ui, 'lineEdit'):
                self.ui.lineEdit.setText(file_display_name)
            elif hasattr(self.ui, 'path'):
                self.ui.path.setText(file_display_name)
                
            # 读取文件内容
            self.read_page_sequence_from_file(file_name)
    
    def toggle_producer(self, producer_index=None):
        # 切换生产者状态
        if hasattr(self.ui, 'producer_2'):
            is_checked = self.ui.producer_2.isChecked()
            
            # 检查是否有动画正在进行
            if hasattr(self, 'animation_in_progress') and self.animation_in_progress:
                # 不显示警告，而是等待动画完成后自动执行操作
                print("动画正在进行中，将在动画完成后自动切换生产者状态")
                
                # 使用单次定时器，延迟执行切换操作
                QtCore.QTimer.singleShot(500, lambda: self._delayed_toggle_producer(producer_index, is_checked))
                
                # 重置按钮状态，保持原状态
                if hasattr(self.ui, 'producer_2'):
                    self.ui.producer_2.setChecked(not is_checked)
                return
            
            # 如果没有动画正在进行，直接执行切换操作
            self._execute_toggle_producer(producer_index, is_checked)

    def _delayed_toggle_producer(self, producer_index, is_checked):
        """延迟执行切换生产者状态的操作"""
        # 检查动画是否仍在进行
        if hasattr(self, 'animation_in_progress') and self.animation_in_progress:
            # 继续等待
            print("动画仍在进行中，继续等待...")
            QtCore.QTimer.singleShot(500, lambda: self._delayed_toggle_producer(producer_index, is_checked))
            return
        
        # 动画已完成，执行切换操作
        print("动画已完成，现在切换生产者状态")
        
        # 更新按钮状态
        if hasattr(self.ui, 'producer_2'):
            self.ui.producer_2.setChecked(is_checked)
        
        # 执行切换操作
        self._execute_toggle_producer(producer_index, is_checked)

    def _execute_toggle_producer(self, producer_index, is_checked):
        # 实际执行切换生产者状态的操作
        # 如果没有指定生产者索引，则切换所有生产者的状态
        if producer_index is None:
            for i in range(len(self.producers_active)):
                self.producers_active[i] = is_checked
                
                # 根据状态启动或停止生产定时器
                if is_checked:
                    # 如果启动生产者，设置定时器间隔并启动定时器
                    if hasattr(self, 'producer_timers') and i < len(self.producer_timers):
                        # 确保每个生产者有不同的启动时间，错开生产
                        # 增加延迟时间到1-2秒，确保生产者之间有足够的间隔
                        delay = random.randint(1000, 2000) * (i + 1)  # 根据索引增加1-2秒的随机延迟
                        
                        # 使用应用了倍数后的速度，并增加基础间隔
                        base_interval = self.producer_speeds[i]
                        # 为每个生产者增加一个额外的随机间隔，确保它们不会同时生产
                        extra_interval = random.randint(500, 1500) * (i + 1)  # 根据索引增加0.5-1.5秒的额外间隔
                        self.producer_timers[i].setInterval(base_interval + extra_interval)
                        
                        # 先停止定时器（如果正在运行）
                        if self.producer_timers[i].isActive():
                            self.producer_timers[i].stop()
                            
                        # 使用单次定时器延迟启动，确保生产者错开启动
                        QtCore.QTimer.singleShot(delay, self.producer_timers[i].start)
                else:
                    # 如果停止生产者，停止定时器，但保留当前页面的生产流程完成
                    if hasattr(self, 'producer_timers') and i < len(self.producer_timers):
                        if self.producer_timers[i].isActive():
                            self.producer_timers[i].stop()
                            
            # 注意：我们不清除producer_current_pages，允许当前动画完成
            
                # 更新按钮文本
                button_text = "停止生产者" if is_checked else "启动生产者"
                self.ui.producer_2.setText(button_text)
        else:
            # 仅切换指定生产者的状态
            if 0 <= producer_index < len(self.producers_active):
                # 切换状态
                self.producers_active[producer_index] = not self.producers_active[producer_index]
                is_active = self.producers_active[producer_index]
                
                # 根据状态启动或停止生产定时器
                if is_active:
                    if hasattr(self, 'producer_timers') and producer_index < len(self.producer_timers):
                        # 使用应用了倍数后的速度，并增加基础间隔
                        base_interval = self.producer_speeds[producer_index]
                        # 为生产者增加一个额外的随机间隔
                        extra_interval = random.randint(500, 1500)  # 0.5-1.5秒的额外间隔
                        self.producer_timers[producer_index].setInterval(base_interval + extra_interval)
                        
                        # 先停止定时器（如果正在运行）
                        if self.producer_timers[producer_index].isActive():
                            self.producer_timers[producer_index].stop()
                        
                        # 延迟启动定时器
                        delay = random.randint(500, 1000)  # 0.5-1秒的随机延迟
                        QtCore.QTimer.singleShot(delay, self.producer_timers[producer_index].start)
                        
                        # 显示生产者激活消息
                        self.show_producer_status_message(producer_index, True)
                else:
                    if hasattr(self, 'producer_timers') and producer_index < len(self.producer_timers):
                        if self.producer_timers[producer_index].isActive():
                            self.producer_timers[producer_index].stop()
                            
                            # 注意：我们不清除producer_current_pages，允许当前动画完成
                            
                        # 显示生产者停止消息
                        self.show_producer_status_message(producer_index, False)
                
                # 更新UI以反映单个生产者的状态变化
                self.update_labels()
        
        # 更新标签
        self.update_labels()
        
        # 重绘场景以反映状态变化
        self.init_scene()
        
        # 如果有提示信息，在生产者激活后移除
        if is_checked and hasattr(self, 'start_hint') and self.start_hint is not None:
            self.scene.removeItem(self.start_hint)
            self.start_hint = None
    
    def show_producer_status_message(self, producer_index, is_active, custom_message=None):
        """显示生产者状态变化的消息"""
        # 计算生产者位置
        producer_x = 275  # 更新为与draw_producers方法中相同的值
        producer_y = 130 + producer_index * 100
        
        # 创建状态消息
        if custom_message:
            status_text = f"生产者{producer_index+1}{custom_message}"
        else:
            status_text = f"生产者{producer_index+1}已{'激活' if is_active else '停止'}"
            
        status_msg = QtWidgets.QGraphicsTextItem(status_text)
        status_msg.setFont(QtGui.QFont("Arial", 10, QtGui.QFont.Bold))
        status_msg.setDefaultTextColor(QtCore.Qt.darkBlue if is_active else QtCore.Qt.darkRed)
        status_msg.setPos(producer_x, producer_y - 20)
        
        # 添加到场景
        self.scene.addItem(status_msg)
        
        # 2秒后移除消息
        QtCore.QTimer.singleShot(2000, lambda: self.safe_remove_items([status_msg]))
    
    def toggle_consumer(self):
        """切换消费者的状态"""
        # 检查是否已经开始模拟
        if not hasattr(self, 'simulation_started') or not self.simulation_started:
            QtWidgets.QMessageBox.warning(self, "操作无效", "请先点击开始模拟按钮！")
            # 重置按钮状态
            if hasattr(self.ui, 'consumer'):
                self.ui.consumer.setChecked(False)
            return
            
        # 获取按钮的检查状态
        if hasattr(self.ui, 'consumer'):
            is_checked = self.ui.consumer.isChecked()
            
            # 检查是否有动画正在进行
            if hasattr(self, 'animation_in_progress') and self.animation_in_progress:
                # 不显示警告，而是等待动画完成后自动执行操作
                print("动画正在进行中，将在动画完成后自动切换消费者状态")
                
                # 使用单次定时器，延迟执行切换操作
                QtCore.QTimer.singleShot(500, lambda: self._delayed_toggle_consumer(is_checked))
                
                # 重置按钮状态，保持原状态
                if hasattr(self.ui, 'consumer'):
                    self.ui.consumer.setChecked(not is_checked)
                return
            
            # 如果没有动画正在进行，直接执行切换操作
            self._execute_toggle_consumer(is_checked)

    def _delayed_toggle_consumer(self, is_checked):
        """延迟执行切换消费者状态的操作"""
        # 检查动画是否仍在进行
        if hasattr(self, 'animation_in_progress') and self.animation_in_progress:
            # 继续等待
            print("动画仍在进行中，继续等待...")
            QtCore.QTimer.singleShot(500, lambda: self._delayed_toggle_consumer(is_checked))
            return
        
        # 动画已完成，执行切换操作
        print("动画已完成，现在切换消费者状态")
        
        # 更新按钮状态
        if hasattr(self.ui, 'consumer'):
            self.ui.consumer.setChecked(is_checked)
        
        # 执行切换操作
        self._execute_toggle_consumer(is_checked)

    def _execute_toggle_consumer(self, is_checked):
        """实际执行切换消费者状态的操作"""
        # 更新消费者状态
        self.consumer_active = is_checked
        
        # 更新按钮文本
        button_text = "停止消费者" if is_checked else "启动消费者"
        self.ui.consumer.setText(button_text)
        
        # 根据状态启动或停止消费定时器
        if is_checked:
            # 如果启动消费者，设置定时器间隔并启动定时器
            self.consumer_timer.setInterval(self.simulation_speed)  # 消费者速度为基础速度
            if not self.consumer_timer.isActive():
                self.consumer_timer.start()
        else:
            # 如果停止消费者，停止定时器，但不清除当前页面或状态
            # 这不会影响生产者继续生产页面和页面动画
            if self.consumer_timer.isActive():
                self.consumer_timer.stop()
        
        # 更新标签
        self.update_labels()
        
        # 重绘场景以反映状态变化
        self.init_scene()
        
        # 如果有提示信息，在消费者激活后移除
        if is_checked and hasattr(self, 'start_hint') and self.start_hint is not None:
            self.scene.removeItem(self.start_hint)
            self.start_hint = None

    def start_simulation(self):
        """开始模拟"""
        try:
            # 初始化状态
            self.producers_active = [False] * self.producer_count
            self.consumer_active = False
            
            # 清空已有数据
            self.buffer = []
            self.memory = []
            self.tlb = []
            self.producer_queues = [[] for _ in range(self.producer_count)]
            self.producer_current_pages = [None] * self.producer_count
            
            # 重置统计数据
            self.total_count = 0
            self.hit_count = 0
            self.miss_count = 0
            self.replace_count = 0
            self.tlb_hit_count = 0
            
            # 重置页面访问记录
            self.page_last_used = {}
            self.page_reference_counts = {}
            
            # 初始化页表
            self.page_table = {}
            
            # 初始化FIFO队列 - 确保它是一个空列表，而不是None
            self.fifo_queue = []
            
            # 移除高亮显示
            if hasattr(self, 'victim_highlight') and self.victim_highlight is not None:
                if self.victim_highlight in self.scene.items():
                    self.scene.removeItem(self.victim_highlight)
                self.victim_highlight = None
            
            # 重置文件页面序列索引
            self.file_page_index = 0
            
            # 设置消费者基础速度
            consumer_base_speed = self.simulation_speed
            
            # 初始化页面动画持续时间
            # 使用horizontalSlider_2的当前值
            if hasattr(self.ui, 'horizontalSlider_2'):
                slider_value = self.ui.horizontalSlider_2.value()
                # 值越大，动画时间越短（越快）
                # 值范围100-1000，映射到动画时间15000-2000毫秒（反向映射）
                animation_duration = int(15000 - (slider_value - 100) * (15000 - 2000) / 900)
                self.page_animation_duration = animation_duration
            else:
                # 默认值
                self.page_animation_duration = 5000
            
            # 为每个生产者设置较慢的初始速度
            self.producer_speeds = []
            self.original_producer_speeds = []  # 保存原始速度
            
            # 获取当前的速度倍数
            multiplier = 1.0  # 默认值
            if hasattr(self, 'producer_speed_multiplier'):
                multiplier = self.producer_speed_multiplier
                
            # 生成原始速度并应用倍数
            for i in range(self.producer_count):
                # 使用消费者速度作为基础，增加一个更大的比例，使生产者速度明显慢于消费者
                # 生产者速度是消费者速度的3.0-6.0倍（更慢），且根据索引增加差异
                base_variation = random.uniform(3.0, 6.0)
                # 根据生产者索引增加额外的时间差异，确保生产者之间的时间间隔足够大
                index_variation = i * 1.0  # 每个生产者增加1.0的基础变化
                variation = base_variation + index_variation
                
                # 计算原始速度
                original_speed = int(consumer_base_speed * variation)
                self.original_producer_speeds.append(original_speed)  # 保存原始速度
                
                # 应用倍数计算实际速度
                actual_speed = int(original_speed * multiplier)
                # 为每个生产者增加一个额外的随机间隔，确保它们不会同时生产
                extra_interval = random.randint(1000, 2000) * (i + 1)  # 根据索引增加1-2秒的额外间隔
                actual_speed += extra_interval
                
                # 限制在合理范围内
                actual_speed = max(100, min(8000, actual_speed))
                self.producer_speeds.append(actual_speed)
                
                # 输出调试信息
                print(f"生产者{i+1}初始化速度: {actual_speed}ms (原始: {original_speed}ms, 倍数: {multiplier:.1f}, 额外间隔: {extra_interval}ms)")
            
            # 确保定时器列表长度与生产者数量一致
            # 先清空现有定时器
            if hasattr(self, 'producer_timers'):
                for timer in self.producer_timers:
                    if timer.isActive():
                        timer.stop()
            
            # 创建新的定时器列表
            self.producer_timers = []
            for i in range(self.producer_count):
                # 为每个生产者创建独立的定时器
                timer = QTimer(self)  # 确保定时器有父对象
                # 使用functools.partial确保每个定时器调用正确的生产者索引
                import functools
                timer.timeout.connect(functools.partial(self.produce_page_for_producer, i))
                timer.setInterval(self.producer_speeds[i])  # 设置初始间隔
                self.producer_timers.append(timer)
            
            # 停止消费者定时器
            if hasattr(self, 'consumer_timer') and self.consumer_timer:
                if self.consumer_timer.isActive():
                    self.consumer_timer.stop()
            
            # 重新创建消费者定时器
            self.consumer_timer = QTimer(self)
            self.consumer_timer.timeout.connect(self.consume_page)
            self.consumer_timer.setInterval(consumer_base_speed)  # 使用消费者速度
            
            # 设置按钮状态
            if hasattr(self.ui, 'producer_2'):
                self.ui.producer_2.setChecked(False)
                self.ui.producer_2.setText("启动生产者")
                # 启用生产者按钮
                self.ui.producer_2.setEnabled(True)
            
            if hasattr(self.ui, 'consumer'):
                self.ui.consumer.setChecked(False)
                self.ui.consumer.setText("启动消费者")
                # 启用消费者按钮
                self.ui.consumer.setEnabled(True)
            
            # 禁用开始按钮，防止重复点击
            if hasattr(self.ui, 'pushButton'):
                self.ui.pushButton.setEnabled(False)
            
            # 启用重置按钮
            if hasattr(self.ui, 'reset_button'):
                self.ui.reset_button.setEnabled(True)
            
            # 更新标签
            self.update_labels()
            
            # 设置模拟已开始标志
            self.simulation_started = True
            
            # 初始化场景
            self.init_scene()
            
            # 更新提示文本
            if hasattr(self, 'start_hint') and self.start_hint is not None:
                if self.start_hint in self.scene.items():
                    self.scene.removeItem(self.start_hint)
                
            self.start_hint = QtWidgets.QGraphicsTextItem("请点击启动生产者和消费者按钮开始模拟")
            self.start_hint.setFont(QtGui.QFont("Arial", 14, QtGui.QFont.Bold))
            self.start_hint.setDefaultTextColor(QtCore.Qt.darkBlue)
            # 放置在场景中央
            scene_width = self.scene.width()
            scene_height = self.scene.height()
            hint_width = self.start_hint.boundingRect().width()
            self.start_hint.setPos((scene_width - hint_width) / 2, 30)
            self.scene.addItem(self.start_hint)
        
        except Exception as e:
            print(f"启动模拟时出错：{str(e)}")
            traceback.print_exc()
    
    def reset_simulation(self):
        """重置模拟"""
        try:
            # 停止所有定时器
            for timer in self.producer_timers:
                if timer.isActive():
                    timer.stop()
            
            if hasattr(self, 'consumer_timer') and self.consumer_timer:
                if self.consumer_timer.isActive():
                    self.consumer_timer.stop()
            
            # 重置状态
            self.producers_active = [False] * self.producer_count
            self.consumer_active = False
            
            # 清空数据
            self.buffer = []
            self.memory = []
            self.tlb = []
            self.producer_queues = [[] for _ in range(self.producer_count)]
            self.producer_current_pages = [None] * self.producer_count
            
            # 重置统计数据
            self.total_count = 0
            self.hit_count = 0
            self.miss_count = 0
            self.replace_count = 0
            self.tlb_hit_count = 0
            
            # 重置页面访问记录
            self.page_last_used = {}
            self.page_reference_counts = {}
            
            # 重置页表
            self.page_table = {}
            
            # 重置FIFO队列 - 确保它是一个空列表，而不是None
            self.fifo_queue = []
            
            # 移除高亮显示
            if hasattr(self, 'victim_highlight') and self.victim_highlight is not None:
                if self.victim_highlight in self.scene.items():
                    self.scene.removeItem(self.victim_highlight)
                self.victim_highlight = None
            
            # 重置文件页面序列索引
            self.file_page_index = 0
            
            # 重置按钮状态
            if hasattr(self.ui, 'producer_2'):
                self.ui.producer_2.setChecked(False)
                self.ui.producer_2.setText("启动生产者")
                # 禁用生产者按钮，直到再次点击开始模拟
                self.ui.producer_2.setEnabled(False)
            
            if hasattr(self.ui, 'consumer'):
                self.ui.consumer.setChecked(False)
                self.ui.consumer.setText("启动消费者")
                # 禁用消费者按钮，直到再次点击开始模拟
                self.ui.consumer.setEnabled(False)
            
            # 启用开始按钮
            if hasattr(self.ui, 'pushButton'):
                self.ui.pushButton.setEnabled(True)
            
            # 更新标签
            self.update_labels()
            
            # 重新初始化场景
            self.init_scene()
            
            # 移除提示信息
            if hasattr(self, 'start_hint') and self.start_hint is not None:
                self.scene.removeItem(self.start_hint)
                self.start_hint = None
            
            # 移除缓冲区满提示
            self.remove_buffer_full_warning()
            
            # 重置模拟状态标志
            self.simulation_started = False
            
            # 显示消息框
            # QtWidgets.QMessageBox.information(self, "模拟结束", "模拟已结束！")
            
        except Exception as e:
            print(f"重置模拟时出错：{str(e)}")
            traceback.print_exc()

    def produce_page(self):
        """生产一个页面"""
        try:
            # 检查是否有活跃的生产者
            active_producers = [i for i, active in enumerate(self.producers_active) if active]
            if not active_producers or len(self.buffer) >= self.buffer_size:
                return  # 没有活跃生产者或缓冲区已满
            
            # 随机选择一个活跃的生产者
            producer_index = random.choice(active_producers)
            
            # 根据生产模式决定如何生成页面
            if self.production_mode == "File" and self.file_page_sequence:
                # 从文件序列中获取下一个页面
                if self.file_page_index < len(self.file_page_sequence):
                    page = self.file_page_sequence[self.file_page_index]
                    self.file_page_index += 1
                    
                    # 显示动画 - 页面通过动画添加到缓冲区，而不是直接添加
                    if isinstance(page, tuple):
                        page_number = page[0]
                    else:
                        page_number = page
                    
                    self.animate_page_production(producer_index, page_number)
                    
                    # 如果已经读取完所有页面，循环从头开始
                    if self.file_page_index >= len(self.file_page_sequence):
                        self.file_page_index = 0
            
            else:
                # 生成一个随机页号（已经是纯页号，不包含页内偏移）
                page_number = self.generate_random_page()
                
                # 显示动画 - 页面通过动画添加到缓冲区，而不是直接添加
                self.animate_page_production(producer_index, page_number)
        
        except Exception as e:
            print(f"生产页面错误：{e}")
            import traceback
            traceback.print_exc()

    def consume_page(self):
        """消费者从缓冲区取出一个页面进行处理"""
        try:
            # 检查消费者是否活跃
            if not self.consumer_active:
                return
            
            # 检查是否有动画正在进行
            if hasattr(self, 'animation_in_progress') and self.animation_in_progress:
                print("动画正在进行中，等待当前动画完成...")
                return
                
            # 检查缓冲区是否为空
            if not self.buffer:
                # 缓冲区为空，无法消费
                self.show_status_message("消费者等待：缓冲区为空", "red", 2000)
                return
            
            # 从缓冲区获取一个页面
            page_number = self.buffer.pop(0)
            
            # 检查页面号的类型，如果是元组，则提取第一个元素作为页面号
            if isinstance(page_number, tuple):
                page_number = page_number[0]
            
            # 确保页号不超过16位
            page_number = self.clean_page_number(page_number)
            
            # 页面号右移页内偏移位数，得到纯页号
            pure_page_number = page_number >> self.page_offset_bits
            
            # 更新缓冲区显示
            self.draw_buffer()
            
            # 标记动画正在进行中 - 提前设置标记，防止在查看是否有空闲生产者时立即恢复生产者
            self.animation_in_progress = True
            
            # 查看是否有空闲生产者需要恢复 - 由于已设置animation_in_progress，生产者不会立即恢复
            for i in range(len(self.producers_active)):
                if i < len(self.producer_current_pages) and self.producers_active[i] and self.producer_current_pages[i] is None:
                    self.check_buffer_and_resume(i)
            
            # 检查TLB是否命中
            tlb_hit = False
            memory_index = -1
            tlb_update_info = {}  # 存储TLB更新信息，用于动画完成后更新
            
            # 创建页表如果不存在
            if not hasattr(self, 'page_table') or self.page_table is None:
                self.page_table = {}
            
            # 创建TLB如果不存在
            if not hasattr(self, 'tlb') or self.tlb is None:
                self.tlb = []
            
            # 遍历TLB查找页面
            for i, entry in enumerate(self.tlb):
                if isinstance(entry, tuple):
                    tlb_page, tlb_frame = entry
                    if tlb_page == pure_page_number:
                        tlb_hit = True
                        memory_index = tlb_frame
                        
                        # 记录TLB更新信息，但不立即更新
                        tlb_update_info = {
                            'hit': True,
                            'hit_index': i,
                            'entry': entry
                        }
                        break
                elif isinstance(entry, dict):
                    if entry.get('page') == pure_page_number:
                        tlb_hit = True
                        memory_index = entry.get('frame')

                        # 记录TLB更新信息，但不立即更新
                        tlb_update_info = {
                            'hit': True,
                            'hit_index': i,
                            'entry': entry
                        }
                        break
            
            # 如果TLB未命中，查询页表
            if not tlb_hit:
                # 查看页表中是否有该页面的映射
                if pure_page_number in self.page_table:
                    memory_index = self.page_table[pure_page_number]
                    
                    # 确认内存中该位置有正确的页面
                    if memory_index < len(self.memory) and self.memory[memory_index] == pure_page_number:
                        # 页表命中 - 记录TLB更新信息，但不立即更新
                        self.show_status_message(f"页表命中: 页面 {pure_page_number:04X} 在帧 {memory_index}", "green", 2000)
                        
                        # 记录需要添加到TLB的信息
                        tlb_update_info = {
                            'hit': False,
                            'add_entry': {
                                'page': pure_page_number, 
                                'frame': memory_index,
                                'timestamp': self.total_count
                            }
                        }
                    else:
                        # 页表中的映射无效，需要重新分配
                        memory_index = -1
                        # 从页表中移除无效的映射
                        if pure_page_number in self.page_table:
                            del self.page_table[pure_page_number]
                else:
                    # 页表缺失 - 需要选择物理帧
                    memory_index = -1
                
                # 如果页表缺失或映射无效，需要分配物理帧
                if memory_index == -1:
                    # 检查该页面是否已经在内存中（在不同的帧）
                    found = False
                    for idx, page in enumerate(self.memory):
                        if page == pure_page_number:
                            memory_index = idx
                            found = True
                            self.show_status_message(f"页面 {pure_page_number:04X} 已在内存中的帧 {memory_index}", "green", 2000)
                            break
                    
                    if not found:
                        # 页面不在内存中，增加缺页计数
                        self.miss_count += 1
                        print(f"缺页：页面 {pure_page_number:04X} 不在内存中，缺页数 +1 = {self.miss_count}")
                        self.show_status_message(f"缺页：页面 {pure_page_number:04X} 不在内存中", "red", 2000)
                        
                        # 首先检查是否有空闲的内存帧
                        free_frame_index = -1
                        for i, page in enumerate(self.memory):
                            if page is None:
                                free_frame_index = i
                                break
                        
                        # 如果有空闲帧，使用它
                        if free_frame_index != -1:
                            memory_index = free_frame_index
                            self.show_status_message(f"页面 {pure_page_number:04X} 放入空闲帧 {memory_index}", "green", 2000)
                        else:
                            # 没有空闲帧，使用替换算法选择牺牲页
                            victim_index = self.select_victim_page()
                            
                            # 如果没有可用的内存帧或内存为空，分配新帧
                            if victim_index == -1 or not self.memory:
                                # 添加新帧，但不超过内存块数量限制
                                if len(self.memory) < self.memory_blocks:
                                    victim_index = len(self.memory)
                                    self.memory.append(None)
                                    self.show_status_message(f"添加新内存帧: {victim_index}", "blue", 2000)
                                else:
                                    # 如果已达到内存块数量限制，使用替换算法
                                    victim_index = self.select_victim_page()
                                    self.show_status_message(f"内存已满，使用替换算法选择帧: {victim_index}", "orange", 2000)
                            
                            memory_index = victim_index
                            
                            # 显示页面替换信息
                            victim_page = self.memory[victim_index] if victim_index < len(self.memory) else None
                            if victim_page is not None:
                                # 使用纯页号显示
                                if isinstance(victim_page, int):
                                    victim_pure_page = victim_page
                                elif isinstance(victim_page, tuple):
                                    victim_pure_page = victim_page[0]
                                else:
                                    victim_pure_page = victim_page

                                # 记录需要从TLB中移除的信息，但不立即移除
                                tlb_update_info['remove_frame'] = victim_index
                                
                                # 记录需要从页表中移除的信息，但不立即移除
                                tlb_update_info['remove_page_table'] = victim_pure_page
                        
                        # 将新页面添加到页表 - 确保一个物理帧只映射一个页面
                        # 这个操作可以立即执行，因为它不会影响动画
                        self.page_table[pure_page_number] = memory_index
                        
                        # 记录需要添加到TLB的信息，但不立即添加
                        tlb_update_info['add_entry'] = {
                            'page': pure_page_number, 
                            'frame': memory_index,
                            'timestamp': self.total_count
                        }
            
            # 确保内存有足够空间，但不超过内存块数量限制
            while len(self.memory) <= memory_index and len(self.memory) < self.memory_blocks:
                self.memory.append(None)
            
            # 保存页面和内存索引，等动画完成后再更新内存
            self.pending_memory_update = {
                'page_number': pure_page_number,
                'memory_index': memory_index,
                'tlb_update_info': tlb_update_info  # 添加TLB更新信息
            }
            
            # 暂停消费者定时器，确保一次只处理一个页面
            if hasattr(self, 'consumer_timer') and self.consumer_timer.isActive():
                print(f"暂停消费者定时器，等待页面 {pure_page_number:04X} 动画完成")
                self.consumer_timer.stop()
            
            # 创建一个回调函数，在动画完成后恢复消费者定时器
            def animation_complete_callback():
                # 更新内存
                self.update_memory_after_animation()
                
                # 标记动画已完成
                self.animation_in_progress = False
                
                # 恢复消费者定时器
                if hasattr(self, 'consumer_timer') and self.consumer_active:
                    print(f"页面 {pure_page_number:04X} 动画完成，恢复消费者定时器")
                    self.consumer_timer.start()
                
                # 现在动画已完成，检查是否有生产者需要恢复
                for i in range(len(self.producers_active)):
                    if i < len(self.producer_current_pages) and self.producers_active[i] and self.producer_current_pages[i] is None:
                        # 使用更长的延迟确保状态完全恢复
                        QtCore.QTimer.singleShot(500, lambda idx=i: self.check_buffer_and_resume(idx))
            
            # 动画显示页面消费过程
            self.animate_page_consumption(0, page_number, memory_index, tlb_hit, animation_complete_callback)
            
            # 更新TLB命中率
            self.tlb_hit_count = self.tlb_hit_count + 1 if tlb_hit else self.tlb_hit_count
            self.total_count += 1
            
            # 更新标签
            self.update_labels()
            
        except Exception as e:
            print(f"消费者处理错误：{e}")
            import traceback
            traceback.print_exc()
            
            # 确保出错时也能恢复消费者定时器和清除动画标记
            self.animation_in_progress = False
            if hasattr(self, 'consumer_timer') and self.consumer_active:
                self.consumer_timer.start()

    def animate_page_production(self, producer_index, page_number):
        """为特定生产者生成一个页面并显示动画效果"""
        try:
            # 确保页号不超过16位
            page_number = self.clean_page_number(page_number)
            
            # 设置当前生产者的页面
            self.producer_current_pages[producer_index] = page_number
            
            # 获取场景宽度
            scene_width = self.scene.width()
            
            # 生产者位置和尺寸
            producer_x = 275  # 和draw_producers方法中保持一致
            producer_y = 130 + producer_index * 100
            producer_width = 160
            producer_height = 60
            
            # 缓冲区位置
            buffer_x = int(scene_width * 0.6)
            buffer_y = 100
            
            # 计算矩形初始位置（在生产者内部的中心点）
            rect_x = producer_x + producer_width/2 - 25
            rect_y = producer_y + producer_height/2 - 15
            rect_width = 50
            rect_height = 30
            
            # 添加页面矩形
            page_rect = QtWidgets.QGraphicsRectItem(0, 0, rect_width, rect_height)
            page_rect.setPen(QtGui.QPen(QtCore.Qt.blue, 2))
            page_rect.setBrush(QtGui.QBrush(QtGui.QColor(200, 230, 250)))
            page_rect.setPos(rect_x, rect_y)
            
            # 添加页面编号 - 使用十六进制显示，不足四位用0填充
            page_text = QtWidgets.QGraphicsTextItem(f"{page_number:04X}")
            page_text.setFont(QtGui.QFont("Arial", 10, QtGui.QFont.Bold))
            page_text.setDefaultTextColor(QtCore.Qt.blue)
            
            # 确保页面编号显示在矩形中心
            text_x = rect_x + (rect_width - page_text.boundingRect().width()) / 2
            text_y = rect_y + (rect_height - page_text.boundingRect().height()) / 2
            page_text.setPos(text_x, text_y)
            
            # 将页面添加到场景
            self.scene.addItem(page_rect)
            self.scene.addItem(page_text)
            
            # 创建生产者信息文本 - 在生产者上方，使用十六进制显示页号
            info_text = QtWidgets.QGraphicsTextItem(f"生产者{producer_index+1}生成页面: {page_number:04X}")
            info_text.setFont(QtGui.QFont("Arial", 9))
            info_text.setDefaultTextColor(QtCore.Qt.darkBlue)
            info_text.setPos(producer_x, producer_y - 20)
            
            # 添加到场景
            self.scene.addItem(info_text)
            
            # 创建缓冲区高亮矩形
            buffer_highlight = QtWidgets.QGraphicsRectItem(buffer_x, buffer_y, 120, self.buffer_size * 40 + 30)
            buffer_highlight.setPen(QtGui.QPen(QtCore.Qt.green, 2, QtCore.Qt.DashLine))
            buffer_highlight.setBrush(QtGui.QBrush(QtCore.Qt.transparent))
            buffer_highlight.setOpacity(0)  # 初始不可见
            
            # 创建临时存储动画项目
            animation_items = [page_rect, page_text, info_text, buffer_highlight]
            
            # 根据页面动画持续时间计算各阶段的时间
            # 总动画时间分配: 闪烁20%, 移动60%, 高亮20%
            flash_time = int(self.page_animation_duration * 0.2)
            move_time = int(self.page_animation_duration * 0.6)
            highlight_time = int(self.page_animation_duration * 0.2)
            
            # 闪烁次数和间隔
            flash_max = 6
            flash_interval = int(flash_time / flash_max)
            
            # 移动步数和间隔
            move_steps = 30
            move_interval = int(move_time / move_steps)
            
            # 高亮步数和间隔
            highlight_steps = 10
            highlight_interval = int(highlight_time / highlight_steps)
            
            # 创建闪烁动画，持续时间为flash_time
            flash_timer = QtCore.QTimer(self)
            flash_count = 0
            
            # 移动页面到缓冲区的动画
            move_timer = QtCore.QTimer(self)
            move_step = 0
            
            # 高亮缓冲区动画
            highlight_timer = QtCore.QTimer(self)
            highlight_step = 0
            
            # 闪烁动画
            def flash_animation():
                nonlocal flash_count
                flash_count += 1
                # 交替显示和隐藏
                opacity = 1.0 if flash_count % 2 == 0 else 0.3
                page_rect.setOpacity(opacity)
                page_text.setOpacity(opacity)
                
                if flash_count >= flash_max:
                    flash_timer.stop()
                    # 闪烁结束后开始移动
                    move_timer.start()
            
            # 移动动画
            def move_animation():
                nonlocal move_step
                move_step += 1
                
                # 计算当前位置（线性插值）
                progress = move_step / move_steps
                
                # 计算缓冲区目标位置 - 根据当前缓冲区内页面数量确定
                buffer_items = len(self.buffer)
                target_y = buffer_y + 20 + buffer_items * 40  # 目标位置是缓冲区的下一个空位
                
                # 矩形起始和结束位置
                rect_start_x = rect_x  # 矩形初始X坐标
                rect_start_y = rect_y  # 矩形初始Y坐标
                rect_end_x = buffer_x + 10  # 矩形终点X坐标
                rect_end_y = target_y  # 矩形终点Y坐标
                
                # 文本起始和结束位置
                text_start_x = text_x  # 初始文本X坐标
                text_start_y = text_y  # 初始文本Y坐标
                text_end_x = rect_end_x + (rect_width - page_text.boundingRect().width()) / 2  # 确保文本在矩形中心
                text_end_y = rect_end_y + (rect_height - page_text.boundingRect().height()) / 2  # 确保文本在矩形中心
                
                # 计算当前矩形位置
                current_rect_x = rect_start_x + (rect_end_x - rect_start_x) * progress
                current_rect_y = rect_start_y + (rect_end_y - rect_start_y) * progress
                
                # 计算当前文本位置
                current_text_x = text_start_x + (text_end_x - text_start_x) * progress
                current_text_y = text_start_y + (text_end_y - text_start_y) * progress
                
                # 更新位置
                page_rect.setPos(current_rect_x, current_rect_y)
                page_text.setPos(current_text_x, current_text_y)
                
                # 动画结束
                if move_step >= move_steps:
                    move_timer.stop()
                    # 移动结束后开始高亮
                    self.scene.addItem(buffer_highlight)
                    highlight_timer.start()
            
            # 高亮动画
            def highlight_animation():
                nonlocal highlight_step
                highlight_step += 1
                
                # 渐变显示高亮
                opacity = highlight_step / highlight_steps
                buffer_highlight.setOpacity(opacity)
                
                # 动画结束
                if highlight_step >= highlight_steps:
                    highlight_timer.stop()
                    # 完成所有动画后进行清理
                    animation_finished()
            
            # 动画结束处理
            def animation_finished():
                # 高亮缓冲区
                # 将页面添加到缓冲区
                self.buffer.append(page_number)
                
                # 更新缓冲区显示
                self.draw_buffer()
                
                # 移除动画项目
                for item in animation_items:
                    if item in self.scene.items():
                        self.scene.removeItem(item)
                
                # 清除生产者当前页面引用，表示生产完成
                self._clear_producer_current_page(producer_index)
                
                # 如果队列中还有页面，显示队列信息
                if self.producer_queues[producer_index]:
                    self.show_producer_queue_info(producer_index)
            
            # 连接定时器信号
            flash_timer.timeout.connect(flash_animation)
            move_timer.timeout.connect(move_animation)
            highlight_timer.timeout.connect(highlight_animation)
            
            # 设置定时器间隔
            flash_timer.setInterval(flash_interval)  # 闪烁间隔
            move_timer.setInterval(move_interval)    # 移动间隔
            highlight_timer.setInterval(highlight_interval)  # 高亮间隔
            
            # 保存定时器，防止被垃圾回收
            if not hasattr(self, 'production_timers'):
                self.production_timers = []
            self.production_timers.extend([flash_timer, move_timer, highlight_timer])
            
            # 开始闪烁动画
            flash_timer.start()
            
        except Exception as e:
            print(f"页面生产动画错误: {e}")
            import traceback
            traceback.print_exc()
            self.show_status_message(f"页面生产动画错误: {str(e)}", "red", 5000)

    def clean_page_number(self, page_number):
        """确保页号不超过16位"""
        return page_number & 0xFFFF

    def _clear_producer_current_page(self, producer_index):
        """清除生产者当前页面的引用，并检查队列中是否有下一个页面"""
        if producer_index < len(self.producer_current_pages):
            self.producer_current_pages[producer_index] = None

    def animate_page_consumption(self, consumer_index, page_number, memory_index, tlb_hit, callback=None):
        """动画显示页面消费过程"""
        try:
            # 确保页号不超过16位
            page_number = self.clean_page_number(page_number)
            
            # 计算纯页号（去除页内偏移）
            pure_page_number = page_number >> self.page_offset_bits
            
            # 获取场景宽度
            scene_width = self.scene.width()
            
            # 计算缓冲区位置
            buffer_x = int(scene_width * 0.6)
            buffer_y = 100
            
            # 计算消费者位置 - 使用draw_consumer中的位置
            consumer_x = int(scene_width * 0.75)  # 从x轴75%的位置开始
            consumer_y = 100
            consumer_width = 160
            consumer_height = 100
            consumer_center_x = consumer_x + consumer_width / 2
            consumer_center_y = consumer_y + consumer_height / 2
            
            # 计算TLB位置 - 使用draw_tlb中的位置
            tlb_x = int(scene_width * 0.9)
            tlb_y = 30
            tlb_width = 250
            tlb_height = min(self.tlb_size, 4) * 30 + 40
            tlb_center_x = tlb_x + tlb_width / 2
            tlb_center_y = tlb_y + tlb_height / 2
            
            # 计算内存位置 - 保持不变
            memory_x = int(scene_width * 0.8)
            memory_y = 250
            memory_width = 100
            memory_height = 40
            rows = 8  # 每列显示8个物理帧
            
            # 计算目标内存帧的位置
            col = memory_index // rows
            row = memory_index % rows
            target_frame_x = memory_x + col * (memory_width + 10)
            target_frame_y = memory_y + row * (memory_height + 10)
            
            # 临时项目列表
            if not hasattr(self, 'temp_items'):
                self.temp_items = []
            
            # 创建页面矩形
            page_rect = QtWidgets.QGraphicsRectItem(0, 0, 60, 40)
            page_rect.setPen(QtGui.QPen(QtCore.Qt.blue, 2))
            page_rect.setBrush(QtGui.QBrush(QtGui.QColor(220, 230, 255)))
            page_rect.setPos(buffer_x + 10, buffer_y + 20)
            
            # 设置页面矩形的z值，确保它在所有其他项目上方
            page_rect.setZValue(10)
            
            self.scene.addItem(page_rect)
            
            # 创建页面文本
            page_text = QtWidgets.QGraphicsTextItem(f"{pure_page_number:04X}")
            page_text.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
            page_text.setDefaultTextColor(QtCore.Qt.darkRed)
            
            # 计算文本位置以居中显示在矩形内
            text_x = buffer_x + 10 + (60 - page_text.boundingRect().width()) / 2
            text_y = buffer_y + 20 + (40 - page_text.boundingRect().height()) / 2
            page_text.setPos(text_x, text_y)
            
            # 设置页面文本的z值，确保它在所有其他项目上方
            page_text.setZValue(11)
            
            self.scene.addItem(page_text)
            
            # 创建高亮矩形
            consumer_highlight = QtWidgets.QGraphicsRectItem(consumer_x, consumer_y, consumer_width, consumer_height)
            consumer_highlight.setPen(QtGui.QPen(QtCore.Qt.darkRed, 3))
            consumer_highlight.setBrush(QtGui.QBrush(QtCore.Qt.transparent))
            consumer_highlight.setOpacity(0.0)  # 初始不可见
            
            tlb_highlight = QtWidgets.QGraphicsRectItem(tlb_x, tlb_y, tlb_width, tlb_height)
            tlb_highlight.setPen(QtGui.QPen(QtCore.Qt.darkBlue, 3))
            tlb_highlight.setBrush(QtGui.QBrush(QtCore.Qt.transparent))
            tlb_highlight.setOpacity(0.0)  # 初始不可见
            
            memory_highlight = QtWidgets.QGraphicsRectItem(target_frame_x, target_frame_y, memory_width, memory_height)
            memory_highlight.setPen(QtGui.QPen(QtCore.Qt.darkGreen, 3))
            memory_highlight.setBrush(QtGui.QBrush(QtCore.Qt.transparent))
            memory_highlight.setOpacity(0.0)  # 初始不可见
            
            # 设置高亮矩形的z值，确保它们在背景但在页面下方
            consumer_highlight.setZValue(5)
            tlb_highlight.setZValue(5)
            memory_highlight.setZValue(5)
            
            # 创建TLB结果文本
            tlb_result_text = QtWidgets.QGraphicsTextItem()
            if tlb_hit:
                tlb_result_text.setPlainText(f"TLB命中")
                tlb_result_text.setDefaultTextColor(QtCore.Qt.darkGreen)
            else:
                tlb_result_text.setPlainText(f"TLB未命中")
                tlb_result_text.setDefaultTextColor(QtCore.Qt.darkRed)
            tlb_result_text.setFont(QtGui.QFont("Arial", 10, QtGui.QFont.Bold))
            tlb_result_text.setZValue(11)  # 确保文本在最上层
            self.temp_items.append(tlb_result_text)
            
            # 创建定时器用于动画
            # 闪烁效果
            flash_timer = QtCore.QTimer(self)
            flash_count = 0
            flash_max = 6
            
            # 第一阶段：缓冲区到消费者
            phase1_timer = QtCore.QTimer(self)
            phase1_step = 0
            phase1_steps = 30
            
            # 第二阶段：消费者到TLB
            phase2_timer = QtCore.QTimer(self)
            phase2_step = 0
            phase2_steps = 30
            
            # 第三阶段：TLB到内存
            phase3_timer = QtCore.QTimer(self)
            phase3_step = 0
            phase3_steps = 30
            
            # 高亮动画
            consumer_highlight_timer = QtCore.QTimer(self)
            consumer_highlight_step = 0
            consumer_highlight_steps = 10
            
            tlb_highlight_timer = QtCore.QTimer(self)
            tlb_highlight_step = 0
            tlb_highlight_steps = 10
            
            memory_highlight_timer = QtCore.QTimer(self)
            memory_highlight_step = 0
            memory_highlight_steps = 10
            
            # 淡出动画
            fade_timer = QtCore.QTimer(self)
            fade_step = 0
            fade_steps = 20
            
            # 闪烁动画
            def flash_animation():
                nonlocal flash_count
                flash_count += 1
                # 交替显示和隐藏
                opacity = 1.0 if flash_count % 2 == 0 else 0.3
                page_rect.setOpacity(opacity)
                page_text.setOpacity(opacity)
                
                if flash_count >= flash_max:
                    flash_timer.stop()
                    # 闪烁结束后开始第一阶段动画
                    phase1_timer.start()
            
            # 第一阶段：缓冲区到消费者的移动
            def phase1_animation():
                nonlocal phase1_step
                phase1_step += 1
                
                # 计算当前位置（线性插值）
                progress = phase1_step / phase1_steps
                
                # 矩形起始和结束位置
                rect_start_x = buffer_x + 10
                rect_start_y = buffer_y + 20
                rect_end_x = consumer_center_x - 30
                rect_end_y = consumer_center_y - 20
                
                # 文本起始和结束位置 - 确保与矩形位置一致
                text_start_x = rect_start_x + (60 - page_text.boundingRect().width()) / 2
                text_start_y = rect_start_y + (40 - page_text.boundingRect().height()) / 2
                text_end_x = rect_end_x + (60 - page_text.boundingRect().width()) / 2
                text_end_y = rect_end_y + (40 - page_text.boundingRect().height()) / 2
                
                # 计算当前位置
                current_rect_x = rect_start_x + (rect_end_x - rect_start_x) * progress
                current_rect_y = rect_start_y + (rect_end_y - rect_start_y) * progress
                current_text_x = text_start_x + (text_end_x - text_start_x) * progress
                current_text_y = text_start_y + (text_end_y - text_start_y) * progress
                
                # 更新位置
                page_rect.setPos(current_rect_x, current_rect_y)
                page_text.setPos(current_text_x, current_text_y)
                
                # 动画结束
                if phase1_step >= phase1_steps:
                    phase1_timer.stop()
                    phase1_complete()
            
            # 第二阶段：消费者到TLB的移动
            def phase2_animation():
                nonlocal phase2_step
                phase2_step += 1
                
                # 计算当前位置（线性插值）
                progress = phase2_step / phase2_steps
                
                # 矩形起始和结束位置
                rect_start_x = consumer_center_x - 30
                rect_start_y = consumer_center_y - 20
                rect_end_x = tlb_center_x - 30  # TLB中心位置偏左
                rect_end_y = tlb_center_y - 20  # TLB中心位置偏上
                
                # 文本起始和结束位置
                text_start_x = rect_start_x + (60 - page_text.boundingRect().width()) / 2
                text_start_y = rect_start_y + (40 - page_text.boundingRect().height()) / 2
                text_end_x = rect_end_x + (60 - page_text.boundingRect().width()) / 2
                text_end_y = rect_end_y + (40 - page_text.boundingRect().height()) / 2
                
                # 计算当前位置
                current_rect_x = rect_start_x + (rect_end_x - rect_start_x) * progress
                current_rect_y = rect_start_y + (rect_end_y - rect_start_y) * progress
                current_text_x = text_start_x + (text_end_x - text_start_x) * progress
                current_text_y = text_start_y + (text_end_y - text_start_y) * progress
                
                # 更新位置
                page_rect.setPos(current_rect_x, current_rect_y)
                page_text.setPos(current_text_x, current_text_y)
                
                # 动画结束
                if phase2_step >= phase2_steps:
                    phase2_timer.stop()
                    phase2_complete()
            
            # 第三阶段：TLB到内存的移动
            def phase3_animation():
                nonlocal phase3_step
                phase3_step += 1
                
                # 计算当前位置（线性插值）
                progress = phase3_step / phase3_steps
                
                # 矩形起始和结束位置
                rect_start_x = tlb_center_x - 30
                rect_start_y = tlb_center_y - 20
                rect_end_x = target_frame_x
                rect_end_y = target_frame_y
                
                # 文本起始和结束位置
                text_start_x = rect_start_x + (60 - page_text.boundingRect().width()) / 2
                text_start_y = rect_start_y + (40 - page_text.boundingRect().height()) / 2
                text_end_x = rect_end_x + (memory_width - page_text.boundingRect().width()) / 2
                text_end_y = rect_end_y + (memory_height - page_text.boundingRect().height()) / 2
                
                # 计算当前位置
                current_rect_x = rect_start_x + (rect_end_x - rect_start_x) * progress
                current_rect_y = rect_start_y + (rect_end_y - rect_start_y) * progress
                current_text_x = text_start_x + (text_end_x - text_start_x) * progress
                current_text_y = text_start_y + (text_end_y - text_start_y) * progress
                
                # 更新位置
                page_rect.setPos(current_rect_x, current_rect_y)
                page_text.setPos(current_text_x, current_text_y)
                
                # 打印调试信息
                print(f"第三阶段动画: {phase3_step}/{phase3_steps}, 位置: ({current_rect_x:.1f}, {current_rect_y:.1f}), 起点: ({rect_start_x:.1f}, {rect_start_y:.1f}), 终点: ({rect_end_x:.1f}, {rect_end_y:.1f})")
                
                # 动画结束
                if phase3_step >= phase3_steps:
                    phase3_timer.stop()
                    print("第三阶段动画完成，调用phase3_complete")
                    phase3_complete()
            
            # 第一阶段完成回调
            def phase1_complete():
                # 添加消费者高亮矩形
                self.scene.addItem(consumer_highlight)
                consumer_highlight_timer.start()
                
                # 显示消费者处理信息
                consumer_msg = QtWidgets.QGraphicsTextItem(f"消费者处理页面 {pure_page_number:04X}")
                consumer_msg.setFont(QtGui.QFont("Arial", 8))
                consumer_msg.setDefaultTextColor(QtCore.Qt.darkRed)
                consumer_msg.setPos(consumer_x + 10, consumer_y - 20)
                consumer_msg.setZValue(11)  # 确保文本在最上层
                self.scene.addItem(consumer_msg)
                
                # 标记为临时信息
                self.temp_items.append(consumer_msg)
            
            # 消费者高亮动画
            def consumer_highlight_animation():
                nonlocal consumer_highlight_step
                consumer_highlight_step += 1
                
                # 计算当前不透明度
                opacity = consumer_highlight_step / consumer_highlight_steps * 0.7
                consumer_highlight.setOpacity(opacity)
                
                # 动画结束
                if consumer_highlight_step >= consumer_highlight_steps:
                    consumer_highlight_timer.stop()
                    # 高亮完成后开始第二阶段动画
                    phase2_timer.start()
            
            # 第二阶段完成回调
            def phase2_complete():
                # 添加TLB高亮矩形
                self.scene.addItem(tlb_highlight)
                
                # 显示TLB查询结果
                tlb_result_text.setPos(tlb_x, tlb_y - 20)
                self.scene.addItem(tlb_result_text)
                
                # 打印调试信息
                print("第二阶段完成，启动TLB高亮动画")
                print(f"TLB位置: x={tlb_x}, y={tlb_y}, 宽度={tlb_width}, 高度={tlb_height}")
                print(f"页面当前位置: x={page_rect.pos().x()}, y={page_rect.pos().y()}")
                
                # 确保TLB高亮矩形可见
                tlb_highlight.setOpacity(0.1)  # 设置初始不透明度
                
                # 启动TLB高亮动画
                tlb_highlight_timer.start()
            
            # TLB高亮动画
            def tlb_highlight_animation():
                nonlocal tlb_highlight_step
                tlb_highlight_step += 1
                
                # 计算当前不透明度
                opacity = tlb_highlight_step / tlb_highlight_steps * 0.7
                tlb_highlight.setOpacity(opacity)
                
                # 打印调试信息
                print(f"TLB高亮动画: {tlb_highlight_step}/{tlb_highlight_steps}, 不透明度: {opacity:.2f}")
                
                # 动画结束
                if tlb_highlight_step >= tlb_highlight_steps:
                    tlb_highlight_timer.stop()
                    # 高亮完成后开始第三阶段动画
                    print("TLB高亮动画完成，启动第三阶段动画")
                    # 确保第三阶段动画定时器存在并正确设置
                    if phase3_timer is not None:
                        # 重置第三阶段步骤计数器
                        nonlocal phase3_step
                        phase3_step = 0
                        # 启动第三阶段动画
                        phase3_timer.start()
                    else:
                        print("错误: phase3_timer不存在!")
            
            # 第三阶段完成回调
            def phase3_complete():
                # 添加内存帧高亮矩形
                self.scene.addItem(memory_highlight)
                memory_highlight_timer.start()
                
                # 调整页面大小和位置以完全匹配内存帧
                page_rect.setRect(0, 0, memory_width, memory_height)
                page_rect.setScale(1.0)
                page_rect.setPos(target_frame_x, target_frame_y)
                
                # 调整文本位置
                page_text.setPos(
                    target_frame_x + (memory_width - page_text.boundingRect().width()) / 2,
                    target_frame_y + (memory_height - page_text.boundingRect().height()) / 2
                )
                
                # 显示替换信息
                # 添加到内存帧旁边的文本
                memory_msg = QtWidgets.QGraphicsTextItem(f"帧 {memory_index}: 页 {pure_page_number:04X}")
                # memory_msg.setFont(QtGui.QFont("Arial", 9))
                # memory_msg.setDefaultTextColor(QtCore.Qt.darkBlue)
                # memory_msg.setPos(target_frame_x + memory_width + 5, target_frame_y + 5)
                # memory_msg.setZValue(11)  # 确保文本在最上层
                # self.scene.addItem(memory_msg)
                self.temp_items.append(memory_msg)
                
                # 打印调试信息
                print(f"第三阶段完成 - 页面 {pure_page_number:04X} 已移动到内存帧 {memory_index}")
                print(f"页面最终位置: x={page_rect.pos().x()}, y={page_rect.pos().y()}")
                
                # 如果有回调函数，先执行回调
                if callback:
                    print(f"执行回调函数更新内存: 页面 {pure_page_number:04X} -> 帧 {memory_index}")
                    callback()
                else:
                    print("警告: 没有回调函数来更新内存")
                
                # 启动淡出动画
                QtCore.QTimer.singleShot(1000, lambda: fade_timer.start())
            
            # 内存帧高亮动画
            def memory_highlight_animation():
                nonlocal memory_highlight_step
                memory_highlight_step += 1
                
                # 计算当前不透明度
                opacity = memory_highlight_step / memory_highlight_steps * 0.7
                memory_highlight.setOpacity(opacity)
                
                # 动画结束
                if memory_highlight_step >= memory_highlight_steps:
                    memory_highlight_timer.stop()
            
            # 淡出动画
            def fade_animation():
                nonlocal fade_step
                fade_step += 1
                
                # 计算当前不透明度
                opacity = 1.0 - fade_step / fade_steps
                
                # 应用到所有临时项目
                page_rect.setOpacity(opacity)
                page_text.setOpacity(opacity)
                consumer_highlight.setOpacity(opacity * 0.7)
                tlb_highlight.setOpacity(opacity * 0.7)
                memory_highlight.setOpacity(opacity * 0.7)
                
                # 动画结束
                if fade_step >= fade_steps:
                    fade_timer.stop()
                    
                    # 清理场景中的所有临时元素
                    items_to_remove = [page_rect, page_text, consumer_highlight, tlb_highlight, memory_highlight, tlb_result_text]
                    items_to_remove.extend(self.temp_items)
                    self.safe_remove_items(items_to_remove)
                    self.temp_items = []
            
            # 设置定时器间隔
            flash_timer.setInterval(100)  # 闪烁间隔100毫秒
            phase1_timer.setInterval(20)  # 第一阶段动画间隔
            phase2_timer.setInterval(20)  # 第二阶段动画间隔
            phase3_timer.setInterval(20)  # 第三阶段动画间隔
            consumer_highlight_timer.setInterval(30)  # 消费者高亮动画间隔
            tlb_highlight_timer.setInterval(30)  # TLB高亮动画间隔
            memory_highlight_timer.setInterval(30)  # 内存帧高亮动画间隔
            fade_timer.setInterval(25)  # 淡出动画间隔
            
            # 连接信号
            flash_timer.timeout.connect(flash_animation)
            phase1_timer.timeout.connect(phase1_animation)
            phase2_timer.timeout.connect(phase2_animation)
            phase3_timer.timeout.connect(phase3_animation)
            consumer_highlight_timer.timeout.connect(consumer_highlight_animation)
            tlb_highlight_timer.timeout.connect(tlb_highlight_animation)
            memory_highlight_timer.timeout.connect(memory_highlight_animation)
            fade_timer.timeout.connect(fade_animation)
            
            # 启动闪烁动画
            flash_timer.start()
            
            # 保存定时器，防止被垃圾回收
            if not hasattr(self, 'animation_timers'):
                self.animation_timers = []
            self.animation_timers.extend([
                flash_timer, phase1_timer, phase2_timer, phase3_timer,
                consumer_highlight_timer, tlb_highlight_timer, memory_highlight_timer,
                fade_timer
            ])
            
        except Exception as e:
            print(f"消费者动画错误：{e}")
            import traceback
            traceback.print_exc()
            self.show_status_message(f"消费者动画错误: {str(e)}", "red", 5000)
            # 确保清理临时项目
            if hasattr(self, 'temp_items'):
                self.safe_remove_items(self.temp_items)
                self.temp_items = []
            # 如果发生错误但仍有回调，尝试执行回调
            if callback:
                callback()

    def init_scene(self):
        # 初始化场景
        self.scene = QtWidgets.QGraphicsScene(self)
        
        # 设置较大的初始场景大小
        scene_width = 1200  # 增加场景宽度
        scene_height = 800
        self.scene.setSceneRect(0, 0, scene_width, scene_height)
        
        # 清除之前的临时动画项
        if hasattr(self, 'temp_animation_items'):
            self.safe_remove_items(self.temp_animation_items)
        self.temp_animation_items = []
        
        # 设置视图
        self.ui.graphicsView.setScene(self.scene)
        self.ui.graphicsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.ui.graphicsView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.ui.graphicsView.setRenderHint(QtGui.QPainter.Antialiasing)
        self.ui.graphicsView.setRenderHint(QtGui.QPainter.SmoothPixmapTransform)
        self.ui.graphicsView.setRenderHint(QtGui.QPainter.TextAntialiasing)
        
        # 禁用滚动条
        self.ui.graphicsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.ui.graphicsView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        
        # 适应视图
        self.ui.graphicsView.fitInView(self.scene.sceneRect(), QtCore.Qt.KeepAspectRatio)
        
        # 延迟调整视图大小
        QtCore.QTimer.singleShot(100, lambda: self.ui.graphicsView.fitInView(self.scene.sceneRect(), QtCore.Qt.KeepAspectRatio))
        
        # 初始化各种图形元素
        self.draw_producers()
        self.draw_consumer()
        self.draw_buffer()
        self.draw_tlb()
        self.draw_memory()
        
        # 更新场景
        self.scene.update()
        
        # 保存当前的start_hint引用
        current_hint = None
        if hasattr(self, 'start_hint') and self.start_hint is not None:
            current_hint = self.start_hint
            
        # 如果模拟已经开始，不显示提示信息
        if hasattr(self, 'simulation_started') and self.simulation_started:
            if current_hint is not None and current_hint in self.scene.items():
                self.scene.removeItem(current_hint)
                self.start_hint = None
        else:
            # 如果模拟尚未开始，显示提示信息
            if current_hint is not None and current_hint in self.scene.items():
                self.scene.removeItem(current_hint)
                
            self.start_hint = QtWidgets.QGraphicsTextItem("请点击开始模拟按钮")
            self.start_hint.setFont(QtGui.QFont("Arial", 14, QtGui.QFont.Bold))
            self.start_hint.setDefaultTextColor(QtCore.Qt.darkBlue)
            # 放置在场景中央
            hint_width = self.start_hint.boundingRect().width()
            self.start_hint.setPos((scene_width - hint_width) / 2, 30)
            self.scene.addItem(self.start_hint)

    def scene_mouse_press_event(self, event):
        """处理场景的鼠标点击事件"""
        # 将视图坐标转换为场景坐标
        scene_pos = self.ui.graphicsView.mapToScene(event.pos())
        
        # 检查是否点击了生产者
        producer_x = 250
        producer_width = 160
        producer_height = 80
        
        for i in range(self.producer_count):
            producer_y = 130 + i * 100
            
            # 检查点击位置是否在生产者矩形内
            if (producer_x <= scene_pos.x() <= producer_x + producer_width and 
                producer_y <= scene_pos.y() <= producer_y + producer_height):
                # 切换该生产者的状态
                self.toggle_producer(i)
                return
        
        # 调用原始的鼠标点击事件处理
        QtWidgets.QGraphicsView.mousePressEvent(self.ui.graphicsView, event)

    def draw_producers(self):
        """绘制生产者"""
        # 获取视图尺寸
        producer_x = 275

        # 计算间距
        producer_width = 160
        producer_height = 80
        vertical_spacing = 100

        # 标题
        title = QtWidgets.QGraphicsTextItem("生产者:点击即可切换状态")
        title.setFont(QtGui.QFont("Arial", 14, QtGui.QFont.Bold))
        title.setDefaultTextColor(QtCore.Qt.blue)
        title.setPos(producer_x + (producer_width - title.boundingRect().width()) / 2, 80)
        self.scene.addItem(title)

        # 绘制生产者方框
        for i in range(self.producer_count):
            # 计算位置
            y_pos = 130 + i * vertical_spacing
            
            # 生产者方框
            producer_rect = QtWidgets.QGraphicsRectItem(producer_x, y_pos, producer_width, producer_height)
            producer_rect.setPen(QtGui.QPen(QtCore.Qt.blue, 2))
            
            # 根据活跃状态设置填充颜色
            if i < len(self.producers_active) and self.producers_active[i]:
                producer_rect.setBrush(QtGui.QBrush(QtGui.QColor(200, 220, 255)))
            else:
                producer_rect.setBrush(QtGui.QBrush(QtGui.QColor(240, 240, 240)))
                
            self.scene.addItem(producer_rect)
            
            # 生产者标签
            producer_label = QtWidgets.QGraphicsTextItem(f"生产者 {i+1}")
            producer_label.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
            producer_label.setDefaultTextColor(QtCore.Qt.darkBlue)
            
            # 居中放置标签
            label_x = producer_x + (producer_width - producer_label.boundingRect().width()) / 2
            producer_label.setPos(label_x, y_pos + 15)
            self.scene.addItem(producer_label)
            
            # 显示状态
            status_text = "活跃" if (i < len(self.producers_active) and self.producers_active[i]) else "休眠"
            status_label = QtWidgets.QGraphicsTextItem(status_text)
            status_label.setFont(QtGui.QFont("Arial", 10, QtGui.QFont.Bold))
            status_label.setDefaultTextColor(
                QtCore.Qt.darkGreen if status_text == "活跃" else QtCore.Qt.darkRed
            )
            
            # 居中放置状态标签
            status_x = producer_x + (producer_width - status_label.boundingRect().width()) / 2
            status_label.setPos(status_x, y_pos + 40)
            self.scene.addItem(status_label)
            
            # 显示生产时间
            if i < len(self.producer_speeds):
                time_text = f"生产时间: {self.producer_speeds[i]/1000:.1f}秒/页"
                time_label = QtWidgets.QGraphicsTextItem(time_text)
                time_label.setFont(QtGui.QFont("Arial", 9))
                time_label.setDefaultTextColor(QtCore.Qt.darkBlue)
                
                # 居中放置时间标签
                time_x = producer_x + (producer_width - time_label.boundingRect().width()) / 2
                time_label.setPos(time_x, y_pos + 60)
                self.scene.addItem(time_label)
            
            # # 如果生产者有正在显示的页面，在右侧显示
            if i < len(self.producer_current_pages) and self.producer_current_pages[i] is not None:
                page_number = self.producer_current_pages[i]

                # 页面框位置
                page_rect_x = producer_x + producer_width + 20
                page_rect_y = y_pos + 10
                page_rect_width = 60
                page_rect_height = 40

                # 添加页面框
                page_rect = QtWidgets.QGraphicsRectItem(page_rect_x, page_rect_y, page_rect_width, page_rect_height)
                page_rect.setPen(QtGui.QPen(QtCore.Qt.blue, 2))
                page_rect.setBrush(QtGui.QBrush(QtGui.QColor(220, 230, 255)))
                self.scene.addItem(page_rect)

                # 添加页面地址（16进制）
                address_label = QtWidgets.QGraphicsTextItem(f"{page_number:04X}")
                address_label.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
                address_label.setDefaultTextColor(QtCore.Qt.darkRed)

                # 居中放置地址标签
                addr_x = page_rect_x + (page_rect_width - address_label.boundingRect().width()) / 2
                addr_y = page_rect_y + (page_rect_height - address_label.boundingRect().height()) / 2
                address_label.setPos(addr_x, addr_y)
                self.scene.addItem(address_label)

                # 添加箭头从生产者到页面
                arrow = QtWidgets.QGraphicsLineItem(producer_x + producer_width, y_pos + producer_height/2,
                                                  page_rect_x, page_rect_y + page_rect_height/2)
                arrow.setPen(QtGui.QPen(QtCore.Qt.blue, 2))
                self.scene.addItem(arrow)

                # 添加箭头头部
                arrow_head = QtWidgets.QGraphicsPolygonItem()
                arrow_polygon = QtGui.QPolygonF()
                arrow_size = 8
                arrow_polygon.append(QtCore.QPointF(page_rect_x, page_rect_y + page_rect_height/2))
                arrow_polygon.append(QtCore.QPointF(page_rect_x - arrow_size, page_rect_y + page_rect_height/2 - arrow_size/2))
                arrow_polygon.append(QtCore.QPointF(page_rect_x - arrow_size, page_rect_y + page_rect_height/2 + arrow_size/2))
                arrow_head.setPolygon(arrow_polygon)
                arrow_head.setBrush(QtGui.QBrush(QtCore.Qt.blue))
                self.scene.addItem(arrow_head)

    def draw_buffer(self):
        """绘制缓冲区"""
        # 获取场景宽度
        scene_width = self.scene.width()
        
        # 位置和尺寸计算
        buffer_x = int(scene_width * 0.6)  # 从x轴60%的位置开始
        buffer_y = 100
        buffer_width = 120
        buffer_height = self.buffer_size * 40 + 30
        
        # 移除旧的缓冲区显示
        if hasattr(self, 'buffer_items') and self.buffer_items:
            self.safe_remove_items(self.buffer_items)
        
        # 创建新的缓冲区项目列表
        self.buffer_items = []
        
        # 标题
        title = QtWidgets.QGraphicsTextItem("缓冲区")
        title.setFont(QtGui.QFont("Arial", 14, QtGui.QFont.Bold))
        title.setDefaultTextColor(QtCore.Qt.darkGreen)
        # 绘制标题至合适的位置
        title.setPos(buffer_x + buffer_width/2 - title.boundingRect().width()/2, 60)
        self.scene.addItem(title)
        self.buffer_items.append(title)

        # 当前状态信息
        used = len(self.buffer)
        
        # 绘制缓冲区
        buffer_rect = QtWidgets.QGraphicsRectItem(buffer_x, buffer_y, buffer_width, buffer_height)
        buffer_rect.setPen(QtGui.QPen(QtCore.Qt.darkGreen, 2))
        
        # 根据缓冲区状态设置填充颜色
        if used >= self.buffer_size:
            # 缓冲区已满，使用淡红色背景
            buffer_rect.setBrush(QtGui.QBrush(QtGui.QColor(255, 240, 240)))
        else:
            buffer_rect.setBrush(QtGui.QBrush(QtGui.QColor(240, 255, 240)))
            
        self.scene.addItem(buffer_rect)
        self.buffer_items.append(buffer_rect)
        
        # 缓冲区内页面
        for i, page in enumerate(self.buffer):
            page_number = page[0] if isinstance(page, tuple) else page
            
            page_rect = QtWidgets.QGraphicsRectItem(buffer_x + 10, buffer_y + 20 + i * 40, buffer_width - 20, 30)
            page_rect.setPen(QtGui.QPen(QtCore.Qt.green, 2))
            page_rect.setBrush(QtGui.QBrush(QtGui.QColor(200, 255, 200)))
            self.scene.addItem(page_rect)
            self.buffer_items.append(page_rect)
            
            # 页面号 - 4位十六进制
            page_text = QtWidgets.QGraphicsTextItem(f"{page_number:04X}")
            page_text.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
            page_text.setDefaultTextColor(QtCore.Qt.darkGreen)
            
            # 居中显示
            text_x = buffer_x + 10 + (buffer_width - 20 - page_text.boundingRect().width()) / 2
            text_y = buffer_y + 20 + i * 40 + (30 - page_text.boundingRect().height()) / 2
            page_text.setPos(text_x, text_y)
            self.scene.addItem(page_text)
            self.buffer_items.append(page_text)
        
        # 如果缓冲区已满，显示提示
        if used >= self.buffer_size:
            # 显示警告信息
            warning_text = QtWidgets.QGraphicsTextItem("缓冲区已满!")
            warning_text.setFont(QtGui.QFont("Arial", 10, QtGui.QFont.Bold))
            warning_text.setDefaultTextColor(QtCore.Qt.red)
            warning_text.setPos(buffer_x + buffer_width/2 - warning_text.boundingRect().width()/2, 
                               buffer_y + buffer_height + 10)
            self.scene.addItem(warning_text)
            self.buffer_items.append(warning_text)
            
            # 5秒后移除警告
            QtCore.QTimer.singleShot(5000, lambda: self.safe_remove_items([warning_text]))

    def draw_consumer(self):
        """绘制消费者"""
        # 获取场景宽度
        scene_width = self.scene.width()
        
        # 位置和尺寸计算
        consumer_x = int(scene_width * 0.75)  # 从x轴75%的位置开始
        consumer_y = 100
        consumer_width = 160
        consumer_height = 100
        
        # 标题
        title = QtWidgets.QGraphicsTextItem("消费者")
        title.setFont(QtGui.QFont("Arial", 14, QtGui.QFont.Bold))
        title.setDefaultTextColor(QtCore.Qt.darkRed)
        # 绘制标题至合适的位置
        title.setPos(consumer_x + consumer_width/2 - title.boundingRect().width()/2, 55)
        self.scene.addItem(title)
        
        # 消费者方框
        consumer_rect = QtWidgets.QGraphicsRectItem(consumer_x, consumer_y, consumer_width, consumer_height)
        consumer_rect.setPen(QtGui.QPen(QtCore.Qt.darkRed, 2))
        
        # 根据活跃状态设置填充颜色
        if self.consumer_active:
            consumer_rect.setBrush(QtGui.QBrush(QtGui.QColor(255, 220, 220)))
        else:
            consumer_rect.setBrush(QtGui.QBrush(QtGui.QColor(240, 240, 240)))
            
        self.scene.addItem(consumer_rect)
        
        # 消费者标签
        consumer_label = QtWidgets.QGraphicsTextItem("内存管理器")
        consumer_label.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
        consumer_label.setDefaultTextColor(QtCore.Qt.darkRed)
        
        # 居中放置标签
        label_x = consumer_x + (consumer_width - consumer_label.boundingRect().width()) / 2
        consumer_label.setPos(label_x, consumer_y + 20)
        self.scene.addItem(consumer_label)
        
        # 显示状态
        status_text = "活跃" if self.consumer_active else "休眠"
        status_label = QtWidgets.QGraphicsTextItem(status_text)
        status_label.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
        status_label.setDefaultTextColor(
            QtCore.Qt.darkGreen if status_text == "活跃" else QtCore.Qt.darkRed
        )
        
        # 居中放置状态标签
        status_x = consumer_x + (consumer_width - status_label.boundingRect().width()) / 2
        status_label.setPos(status_x, consumer_y + 60)
        self.scene.addItem(status_label)

    def draw_tlb(self):
        """绘制TLB"""
        # 获取场景宽度
        scene_width = self.scene.width()
        
        # 位置和尺寸计算
        tlb_x = int(scene_width * 0.9)
        tlb_y = 30
        tlb_width = 250
        tlb_height = min(self.tlb_size, 4) * 30 + 40
        
        # 移除旧的TLB显示
        if hasattr(self, 'tlb_items'):
            for item in self.tlb_items:
                if item in self.scene.items():
                    self.scene.removeItem(item)
        
        # 创建新的TLB项目列表
        self.tlb_items = []
        
        # 标题
        title = QtWidgets.QGraphicsTextItem("TLB")
        title.setFont(QtGui.QFont("Arial", 14, QtGui.QFont.Bold))
        title.setDefaultTextColor(QtCore.Qt.darkBlue)
        title.setPos(tlb_x + tlb_width/2 - 30, tlb_y - 30)
        self.scene.addItem(title)
        self.tlb_items.append(title)
        
        # TLB表格
        tlb_rect = QtWidgets.QGraphicsRectItem(tlb_x, tlb_y, tlb_width, tlb_height)
        tlb_rect.setPen(QtGui.QPen(QtCore.Qt.darkBlue, 2))
        tlb_rect.setBrush(QtGui.QBrush(QtGui.QColor(230, 230, 255)))
        self.scene.addItem(tlb_rect)
        self.tlb_items.append(tlb_rect)
        
        # 表头
        header_page = QtWidgets.QGraphicsTextItem("页号")
        header_page.setFont(QtGui.QFont("Arial", 10, QtGui.QFont.Bold))
        header_page.setDefaultTextColor(QtCore.Qt.darkBlue)
        header_page.setPos(tlb_x + 10, tlb_y + 6)
        self.scene.addItem(header_page)
        self.tlb_items.append(header_page)
        
        header_frame = QtWidgets.QGraphicsTextItem("物理块号")
        header_frame.setFont(QtGui.QFont("Arial", 10, QtGui.QFont.Bold))
        header_frame.setDefaultTextColor(QtCore.Qt.darkBlue)
        header_frame.setPos(tlb_x + 95, tlb_y + 6)
        self.scene.addItem(header_frame)
        self.tlb_items.append(header_frame)
        
        header_time = QtWidgets.QGraphicsTextItem("时间戳")
        header_time.setFont(QtGui.QFont("Arial", 10, QtGui.QFont.Bold))
        header_time.setDefaultTextColor(QtCore.Qt.darkBlue)
        header_time.setPos(tlb_x + 180, tlb_y + 6)
        self.scene.addItem(header_time)
        self.tlb_items.append(header_time)
        
        # 绘制分隔线
        separator = QtWidgets.QGraphicsLineItem(tlb_x, tlb_y + 30, tlb_x + tlb_width, tlb_y + 30)
        separator.setPen(QtGui.QPen(QtCore.Qt.darkBlue, 1))
        self.scene.addItem(separator)
        self.tlb_items.append(separator)
        
        # 显示TLB内容
        for i in range(min(len(self.tlb), self.tlb_size)):
            entry = self.tlb[i]
            
            # 处理不同的TLB条目格式
            if isinstance(entry, tuple):
                page_number = entry[0]
                frame_number = entry[1]
                timestamp = self.total_count - i  # 根据位置估算时间戳
            elif isinstance(entry, dict):
                page_number = entry.get('page', 0)
                frame_number = entry.get('frame', 0)
                timestamp = entry.get('timestamp', self.total_count - i)
            else:
                continue  # 跳过无法识别的格式
            
            # 绘制行背景 - 奇偶行使用不同颜色
            row_bg = QtWidgets.QGraphicsRectItem(tlb_x, tlb_y + 30 + i * 30, tlb_width, 30)
            row_bg.setPen(QtGui.QPen(QtCore.Qt.transparent))
            if i % 2 == 0:
                row_bg.setBrush(QtGui.QBrush(QtGui.QColor(240, 240, 255)))
            else:
                row_bg.setBrush(QtGui.QBrush(QtGui.QColor(250, 250, 255)))
            self.scene.addItem(row_bg)
            self.tlb_items.append(row_bg)
            
            # 页号 - 4位十六进制
            page_text = QtWidgets.QGraphicsTextItem(f"{page_number:04X}")
            page_text.setFont(QtGui.QFont("Arial", 10, QtGui.QFont.Bold))
            page_text.setDefaultTextColor(QtCore.Qt.darkBlue)
            page_text.setPos(tlb_x + 20, tlb_y + 35 + i * 30)
            self.scene.addItem(page_text)
            self.tlb_items.append(page_text)
            
            # 绘制映射箭头
            arrow = QtWidgets.QGraphicsTextItem("=>")
            arrow.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
            arrow.setDefaultTextColor(QtCore.Qt.darkGreen)
            arrow.setPos(tlb_x + 85, tlb_y + 30 + i * 30)
            self.scene.addItem(arrow)
            self.tlb_items.append(arrow)
            
            # 物理帧
            frame_text = QtWidgets.QGraphicsTextItem(f"{frame_number:02X}")
            frame_text.setFont(QtGui.QFont("Arial", 10, QtGui.QFont.Bold))
            frame_text.setDefaultTextColor(QtCore.Qt.darkRed)
            frame_text.setPos(tlb_x + 120, tlb_y + 35 + i * 30)
            self.scene.addItem(frame_text)
            self.tlb_items.append(frame_text)
            
            # 时间戳
            time_text = QtWidgets.QGraphicsTextItem(f"{timestamp}")
            time_text.setFont(QtGui.QFont("Arial", 10))
            time_text.setDefaultTextColor(QtCore.Qt.darkGray)
            time_text.setPos(tlb_x + 180, tlb_y + 35 + i * 30)
            self.scene.addItem(time_text)
            self.tlb_items.append(time_text)

    def draw_memory(self):
        """绘制物理内存"""
        # 获取场景宽度
        scene_width = self.scene.width()
        
        # 位置和尺寸计算
        memory_x = int(scene_width * 0.8)  # 从x轴45%的位置开始
        memory_y = 250
        memory_width = 100
        memory_height = 40
        rows = 8  # 每列显示8个物理帧
        
        # 计算列数
        columns = (self.memory_blocks + rows - 1) // rows  # 向上取整
        
        # 标题
        title = QtWidgets.QGraphicsTextItem("物理内存")
        title.setFont(QtGui.QFont("Arial", 14, QtGui.QFont.Bold))
        title.setDefaultTextColor(QtCore.Qt.darkMagenta)
        title.setPos(memory_x + (memory_width * columns) / 2 - 50, memory_y - 30)
        self.scene.addItem(title)

        
        # # 添加已使用标签
        # used_label = QtWidgets.QGraphicsTextItem(f"已使用: {len(self.memory)}/{self.memory_blocks} | 算法: {self.replacement_algorithm}")
        # used_label.setFont(QtGui.QFont("Arial", 10))
        # used_label.setDefaultTextColor(QtCore.Qt.darkMagenta)
        # used_label.setPos(memory_x, memory_y - 30)
        # self.scene.addItem(used_label)
        
        # 绘制内存区域
        for i in range(self.memory_blocks):
            col = i // rows  # 列索引
            row = i % rows   # 行索引
            
            x = memory_x + col * (memory_width + 10)
            y = memory_y + row * (memory_height + 10)
            
            # 内存帧方框
            frame_rect = QtWidgets.QGraphicsRectItem(x, y, memory_width, memory_height)
            frame_rect.setPen(QtGui.QPen(QtCore.Qt.darkMagenta, 2))
            
            # 设置填充颜色
            if i < len(self.memory) and self.memory[i] is not None:
                frame_rect.setBrush(QtGui.QBrush(QtGui.QColor(250, 230, 250)))
            else:
                frame_rect.setBrush(QtGui.QBrush(QtGui.QColor(240, 240, 240)))
            
            self.scene.addItem(frame_rect)
            
            # 帧号
            frame_label = QtWidgets.QGraphicsTextItem(f"帧{i}")
            frame_label.setFont(QtGui.QFont("Arial", 9, QtGui.QFont.Bold))
            frame_label.setDefaultTextColor(QtCore.Qt.darkMagenta)
            frame_label.setPos(x + 5, y + 5)
            self.scene.addItem(frame_label)
            
            # 如果内存帧中有页面，显示页号
            if i < len(self.memory) and self.memory[i] is not None:
                # 页号 - 使用十六进制显示，不足四位用0填充
                page_num = self.memory[i]
                page_text = QtWidgets.QGraphicsTextItem(f"{page_num:04X}")
                page_text.setFont(QtGui.QFont("Arial", 9, QtGui.QFont.Bold))
                page_text.setDefaultTextColor(QtCore.Qt.darkMagenta)
                page_text.setPos(x + 60, y + 5)
                self.scene.addItem(page_text)
                
                # 访问信息（如果有使用LRU或LFU算法）
                if self.replacement_algorithm == "LRU" and page_num in self.page_last_used:
                    time_text = QtWidgets.QGraphicsTextItem(f"T:{self.page_last_used[page_num]}")
                    time_text.setFont(QtGui.QFont("Arial", 8))
                    time_text.setDefaultTextColor(QtCore.Qt.darkGray)
                    time_text.setPos(x + 5, y + 22)
                    self.scene.addItem(time_text)
                
                elif self.replacement_algorithm == "LFU" and page_num in self.page_reference_counts:
                    count_text = QtWidgets.QGraphicsTextItem(f"访问:{self.page_reference_counts[page_num]}")
                    count_text.setFont(QtGui.QFont("Arial", 8))
                    count_text.setDefaultTextColor(QtCore.Qt.darkGray)
                    count_text.setPos(x + 5, y + 22)
                    self.scene.addItem(count_text)

    def read_page_sequence_from_file(self, file_name):
        """从文件中读取页面序列"""
        try:
            with open(file_name, 'r') as file:
                # 读取文件内容
                content = file.read().strip()
                # 解析页面序列
                self.file_page_sequence = []
                for line in content.split('\n'):
                    for num in line.split():
                        try:
                            # 尝试将输入解析为整数
                            page_number = int(num)
                            
                            # 确保页号在合理范围内
                            max_page_number = (2**(self.logical_address_size * 10) // (2**self.page_offset_bits)) - 1
                            if page_number > max_page_number:
                                page_number = page_number % (max_page_number + 1)
                            
                            # 添加到序列 - 直接存储纯页号，不添加页内偏移
                            self.file_page_sequence.append(page_number)
                        except ValueError:
                            # 如果是十六进制数
                            if num.startswith("0x") or num.startswith("0X"):
                                try:
                                    # 解析十六进制数
                                    hex_value = int(num, 16)
                                    
                                    # 提取页号（去掉页内偏移）
                                    page_number = hex_value >> self.page_offset_bits
                                    
                                    # 确保页号在合理范围内
                                    max_page_number = (2**(self.logical_address_size * 10) // (2**self.page_offset_bits)) - 1
                                    if page_number > max_page_number:
                                        page_number = page_number % (max_page_number + 1)
                                    
                                    # 添加到序列 - 只存储纯页号
                                    self.file_page_sequence.append(page_number)
                                except ValueError:
                                    continue
            
            # 如果没有成功解析任何页面，则提示错误
            if not self.file_page_sequence:
                QtWidgets.QMessageBox.warning(self, "文件格式错误", "未能从文件中解析出有效的页面序列")
                return
            
            # 重置文件页面索引
            self.file_page_index = 0
            
            # 更新生产模式
            self.production_mode = "File"
            self.ui.module.setCurrentText("File")
            
            # 更新UI
            self.update_file_path_visibility()
            self.update_labels()
            
            # 重绘场景
            self.init_scene()
        
        except Exception as e:
            import traceback    
            traceback.print_exc()  # 打印详细错误信息
            QtWidgets.QMessageBox.critical(self, "文件读取错误", f"无法读取文件: {str(e)}")

    # 添加鼠标事件处理方法用于窗口拖动
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self._drag_pos = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton and self._drag_pos:
            self.move(event.globalPos() - self._drag_pos)
            event.accept()

    def mouseReleaseEvent(self, event):
        self._drag_pos = None
        event.accept()

    def update_labels(self):
        """更新滑块上方的文字显示和各种统计信息标签"""
        # 更新基本参数标签
        if hasattr(self.ui, 'size'):
            self.ui.size.setText(f"{self.logical_address_size} KB")
        
        if hasattr(self.ui, 'algorithm2'):
            self.ui.algorithm2.setText(f"{self.replacement_algorithm}")
        
        if hasattr(self.ui, 'producerNum'):
            self.ui.producerNum.setText(f"{self.producer_count}")
        
        if hasattr(self.ui, 'buffersize'):
            self.ui.buffersize.setText(f"{self.buffer_size}")
        
        if hasattr(self.ui, 'memory'):
            self.ui.memory.setText(f"{self.memory_blocks}")
        
        # 更新统计信息标签
        if hasattr(self.ui, 'miss'):
            self.ui.miss.setText(f"{self.miss_count}")
        
        if hasattr(self.ui, 'total'):
            self.ui.total.setText(f"{self.total_count}")
        
        # 计算缺页率
        miss_rate = 0 if self.total_count == 0 else (self.miss_count / self.total_count) * 100
        if hasattr(self.ui, 'missrate'):
            self.ui.missrate.setText(f"{miss_rate:.2f}%")
        
        # 更新生产者状态按钮
        if hasattr(self.ui, 'producer_2'):
            button_text = "停止生产者" if any(self.producers_active) else "启动生产者"
            self.ui.producer_2.setText(button_text)
        
        # 更新消费者状态按钮
        if hasattr(self.ui, 'consumer'):
            button_text = "停止消费者" if self.consumer_active else "启动消费者"
            self.ui.consumer.setText(button_text)
        
        # 更新滑块标签值
        # 显示生产者速度
        if hasattr(self.ui, 'label_11'):
            if hasattr(self, 'producer_speed_multiplier'):
                avg_speed = sum(self.producer_speeds) / len(self.producer_speeds) if self.producer_speeds else 0
                self.ui.label_11.setText(f"生产者速度: {self.producer_speed_multiplier:.1f}倍")
            else:
                self.ui.label_11.setText(f"生产者速度: {self.simulation_speed}ms")

    def get_tlb_hit_rate(self):
        """计算TLB命中率"""
        if self.total_count == 0:
            return 0.0
        return (self.tlb_hit_count / self.total_count) * 100

    def generate_random_page(self):
        """生成随机页号 - 基于逻辑地址空间大小（KB单位）
           例如: 1KB = 1024byte = 2^10 = 10位二进制，页号范围为0-1023
                64KB = 65536byte = 2^16 = 16位二进制，页号范围为0-65535
        """
        try:
            # 计算地址空间的位数（KB转换为bit）
            # logical_address_size是KB单位的输入值
            address_bits = int(math.log2(self.logical_address_size * 1024))
            
            # 计算最大页号 - 根据地址空间大小确定范围
            # 例如: 1KB时，最大页号为1023 (2^10-1)
            max_page = (1 << address_bits) - 1
            
            # 随机生成一个在范围内的页号
            page_number = random.randint(0, max_page)
        
            # 返回页号
            return page_number
        except Exception as e:
            # 出现错误时显示警告并返回安全值
            self.show_status_message(
                f"警告: 地址空间计算错误！使用默认页号。",
                "red",
                5000
            )
            print(f"生成随机页号错误: {e}")
            # 返回一个安全的小页号
            return random.randint(0, 15)

    def resizeEvent(self, event):
        """处理窗口大小调整事件"""
        super().resizeEvent(event)
        
        # 如果场景已初始化，则调整视图以适应新的窗口大小
        if hasattr(self, 'scene') and self.scene is not None:
            # 更新场景大小
            view_rect = self.ui.graphicsView.rect()
            scene_width = max(1000, view_rect.width())
            scene_height = max(800, view_rect.height())
            self.scene.setSceneRect(0, 0, scene_width, scene_height)
            
            # 确保视图适应场景大小
            self.ui.graphicsView.setSceneRect(self.scene.sceneRect())
            
            # 调整视图缩放
            self.ui.graphicsView.fitInView(self.scene.sceneRect(), QtCore.Qt.KeepAspectRatio)
            
            # 强制更新
            self.ui.graphicsView.update()
            
            # 在事件队列中安排一个延迟的视图调整，以确保正确渲染
            QtCore.QTimer.singleShot(50, lambda: self.ui.graphicsView.fitInView(self.scene.sceneRect(), QtCore.Qt.KeepAspectRatio))

    def safe_remove_items(self, items):
        """安全地从场景中移除项目"""
        if not items:
            return
        
        for item in items:
            try:
                if item and item.scene() == self.scene:
                    self.scene.removeItem(item)
            except:
                pass
            
    def select_victim_page(self):
        """根据当前替换算法选择要被替换的页面索引"""
        try:
            # 首先检查是否有空闲的内存帧
            for i, page in enumerate(self.memory):
                if page is None:
                    return i
            
            # 如果内存没有填满且没有达到内存块上限，返回新的帧索引
            if len(self.memory) < self.memory_blocks:
                return len(self.memory)
            
            # 所有帧都已被使用，需要选择一个牺牲页
            victim_index = 0  # 默认替换第一个页面
            
            # FIFO算法 - 替换最先进入内存的页面
            if self.replacement_algorithm == "FIFO":
                # 如果没有FIFO队列或队列为空，重新初始化它
                if not hasattr(self, 'fifo_queue') or not self.fifo_queue:
                    # 使用当前内存中的所有有效帧索引初始化队列
                    self.fifo_queue = [i for i, page in enumerate(self.memory) if page is not None]
                    # 如果队列仍然为空，使用第一个帧
                    if not self.fifo_queue:
                        self.fifo_queue = [0] if len(self.memory) > 0 else []
                
                # 确保fifo_queue不为空
                if self.fifo_queue:
                    # 获取队列头部的页面索引（最早进入内存的帧）
                    victim_index = self.fifo_queue[0]
                    
                    # 验证选择的帧索引是否有效
                    if victim_index >= len(self.memory) or self.memory[victim_index] is None:
                        # 如果索引无效，重新初始化队列并选择第一个有效帧
                        self.fifo_queue = [i for i, page in enumerate(self.memory) if page is not None]
                        if self.fifo_queue:
                            victim_index = self.fifo_queue[0]
                        else:
                            victim_index = 0
                else:
                    victim_index = 0
                
            # LRU算法 - 替换最近最少使用的页面
            elif self.replacement_algorithm == "LRU":
                min_time = float('inf')
                
                for i, page in enumerate(self.memory):
                    if page is None:
                        continue  # 跳过空帧
                    # 获取页面最后使用时间，如果不存在则设为0（最早）
                    last_used = self.page_last_used.get(page, 0)
                    if last_used < min_time:
                        min_time = last_used
                        victim_index = i
                        
            # LFU算法 - 替换使用频率最低的页面
            elif self.replacement_algorithm == "LFU":
                min_freq = float('inf')
                
                for i, page in enumerate(self.memory):
                    if page is None:
                        continue  # 跳过空帧
                    # 获取页面使用频率，默认为0
                    freq = self.page_reference_counts.get(page, 0)
                    if freq < min_freq:
                        min_freq = freq
                        victim_index = i
                        
            # 随机替换
            else:
                # 只考虑非空的内存帧
                non_empty_frames = [i for i, page in enumerate(self.memory) if page is not None]
                if non_empty_frames:
                    victim_index = random.choice(non_empty_frames)
                else:
                    # 如果所有帧都是空的，返回第一个帧
                    victim_index = 0
            
            # 高亮显示将被替换的页面
            self.highlight_victim_page(victim_index)
            
            # 输出调试信息
            victim_page = self.memory[victim_index] if victim_index < len(self.memory) else None
            if victim_page is not None:
                print(f"页面置换: 算法={self.replacement_algorithm}, 选择帧={victim_index}, 页面={victim_page}")
            
            return victim_index
            
        except Exception as e:
            print(f"选择牺牲页错误: {e}")
            import traceback
            traceback.print_exc()
            # 默认返回0
            return 0
        
    def highlight_victim_page(self, victim_index):
        """高亮显示将被替换的页面"""
        try:
            # 获取场景宽度
            scene_width = self.scene.width()
            
            # 计算内存位置
            memory_x = int(scene_width * 0.8)
            memory_y = 250
            memory_width = 100
            memory_height = 40
            rows = 8  # 每列显示8个物理帧
            
            # 计算目标内存帧的位置
            col = victim_index // rows
            row = victim_index % rows
            target_frame_x = memory_x + col * (memory_width + 10)
            target_frame_y = memory_y + row * (memory_height + 10)
            
            # 移除之前的高亮
            if hasattr(self, 'victim_highlight') and self.victim_highlight is not None:
                if self.victim_highlight in self.scene.items():
                    self.scene.removeItem(self.victim_highlight)
            
            # 移除之前的文本提示
            if hasattr(self, 'victim_text') and self.victim_text is not None:
                if self.victim_text in self.scene.items():
                    self.scene.removeItem(self.victim_text)
                self.victim_text = None
            
            # 创建新的高亮矩形 - 使用更明显的红色边框
            self.victim_highlight = QtWidgets.QGraphicsRectItem(target_frame_x - 3, target_frame_y - 3, memory_width + 6, memory_height + 6)
            self.victim_highlight.setPen(QtGui.QPen(QtCore.Qt.red, 4))  # 更粗的红色边框
            self.victim_highlight.setBrush(QtGui.QBrush(QtCore.Qt.transparent))
            
            # 添加到场景
            self.scene.addItem(self.victim_highlight)
            
        except Exception as e:
            print(f"高亮受害页面错误: {e}")
            import traceback
            traceback.print_exc()

    def produce_page_for_producer(self, producer_index):
        """为特定生产者生成一个页面"""
        try:
            # 检查是否已经开始模拟
            if not hasattr(self, 'simulation_started') or not self.simulation_started:
                return  # 如果还没有开始模拟，不执行生产

            # 检查是否有动画正在进行
            if hasattr(self, 'animation_in_progress') and self.animation_in_progress:
                print(f"生产者{producer_index+1}等待：动画正在进行中...")
                # 如果有动画正在进行，稍后再检查
                if hasattr(self, 'producer_timers') and producer_index < len(self.producer_timers):
                    # 延迟一段时间后再次尝试
                    QtCore.QTimer.singleShot(500, lambda: self.produce_page_for_producer(producer_index))
                return

            # 检查生产者是否有正在生产的页面
            # 如果有页面正在生产，则等待其完成，无论生产者是否活跃
            if self.producer_current_pages[producer_index] is not None:
                # 如果队列中有页面但生产者被停止，仍然显示队列信息
                if not self.producers_active[producer_index] and self.producer_queues[producer_index]:
                    self.show_producer_queue_info(producer_index)
                return

            # 检查缓冲区是否已满 - 严格检查缓冲区大小
            if len(self.buffer) >= self.buffer_size:
                # 暂停该生产者，防止继续生产
                if hasattr(self, 'producer_timers') and producer_index < len(self.producer_timers):
                    if self.producer_timers[producer_index].isActive():
                        self.producer_timers[producer_index].stop()

                        # 显示警告消息
                        self.show_status_message(f"缓冲区已满，生产者{producer_index+1}暂停", "orange", 2000)

                        # 设置一个定时器，每隔一段时间检查缓冲区是否有空间
                        QtCore.QTimer.singleShot(1000, lambda: self.check_buffer_and_resume(producer_index))

                # 即使缓冲区已满，仍然显示队列信息
                if self.producer_queues[producer_index]:
                    self.show_producer_queue_info(producer_index)
                return
            else:
                # 如果缓冲区不再满，移除提示信息
                self.remove_buffer_full_warning()

            # 检查生产者是否活跃 - 只有活跃的生产者才能生成新页面
            # 但要注意，如果已经有页面在队列中，即使生产者不活跃也应该完成生产
            if producer_index >= len(self.producers_active) or not self.producers_active[producer_index]:
                # 检查队列中是否有页面需要处理
                if producer_index < len(self.producer_queues) and self.producer_queues[producer_index]:
                    page_number = self.producer_queues[producer_index].pop(0)
                    # 设置当前页面并开始动画
                    self.producer_current_pages[producer_index] = page_number
                    self.animate_page_production(producer_index, page_number)

                    # 如果队列中还有页面，显示队列信息
                    if self.producer_queues[producer_index]:
                        self.show_producer_queue_info(producer_index)
                else:
                    # 生产者不活跃且没有待处理页面，停止定时器
                    if hasattr(self, 'producer_timers') and producer_index < len(self.producer_timers) and self.producer_timers[producer_index].isActive():
                        self.producer_timers[producer_index].stop()
                return

            # 生成新页面
            page_number = self.generate_page_for_producer(producer_index)

            # 设置当前页面并开始动画
            self.producer_current_pages[producer_index] = page_number
            self.animate_page_production(producer_index, page_number)

            # 如果队列中有页面，显示队列信息
            if self.producer_queues[producer_index]:
                self.show_producer_queue_info(producer_index)

            # 重置定时器，确保下一次按照设定的时间间隔产生页面
            if hasattr(self, 'producer_timers') and producer_index < len(self.producer_timers) and self.producers_active[producer_index]:
                # 使用应用了倍数后的速度
                self.producer_timers[producer_index].setInterval(self.producer_speeds[producer_index])

        except Exception as e:
            print(f"生产者{producer_index+1}生产页面错误：{e}")
            import traceback
            traceback.print_exc()

            # 发生错误时，确保清除当前页面引用
            if producer_index < len(self.producer_current_pages):
                self.producer_current_pages[producer_index] = None

    def remove_buffer_full_warning(self):
        """移除缓冲区已满警告"""
        if hasattr(self, 'buffer_full_message') and self.buffer_full_message is not None:
            self.scene.removeItem(self.buffer_full_message)
            self.buffer_full_message = None
            
        if hasattr(self, 'buffer_full_bg') and self.buffer_full_bg is not None:
            self.scene.removeItem(self.buffer_full_bg)
            self.buffer_full_bg = None
            
    def check_buffer_and_resume(self, producer_index):
        """检查缓冲区是否有空间，如果有则恢复生产者"""
        try:
            # 检查生产者是否活跃
            if producer_index >= len(self.producers_active) or not self.producers_active[producer_index]:
                return
            
            # 检查是否有动画正在进行
            if hasattr(self, 'animation_in_progress') and self.animation_in_progress:
                # 如果有动画正在进行，稍后再检查
                print(f"动画正在进行中，推迟恢复生产者{producer_index+1}")
                # 延长等待时间，确保动画完全结束
                QtCore.QTimer.singleShot(1000, lambda: self.check_buffer_and_resume(producer_index))
                return
            
            # 检查缓冲区是否已满
            if len(self.buffer) >= self.buffer_size:
                # 缓冲区仍然满，显示警告
                self.show_status_message(f"缓冲区已满，生产者{producer_index+1}等待空间", "orange", 2000)
                
                # 设置一个定时器，每隔一段时间检查缓冲区是否有空间
                QtCore.QTimer.singleShot(1000, lambda: self.check_buffer_and_resume(producer_index))
                return
            
            # 缓冲区有空间，恢复生产者
            if hasattr(self, 'producer_timers') and producer_index < len(self.producer_timers):
                if not self.producer_timers[producer_index].isActive():
                    # 启动生产者定时器
                    self.producer_timers[producer_index].start()
                    
                    # 显示消息
                    self.show_producer_status_message(producer_index, True, "已恢复生产")
                    
            # 移除缓冲区已满警告
            self.remove_buffer_full_warning()
        
        except Exception as e:
            print(f"检查缓冲区并恢复生产者错误：{e}")
            import traceback
            traceback.print_exc()
            
    def show_producer_status_message(self, producer_index, is_active, custom_message=None):
        """显示生产者状态变化的消息"""
        # 计算生产者位置
        producer_x = 275  # 更新为与draw_producers方法中相同的值
        producer_y = 130 + producer_index * 100
        
        # 创建状态消息
        if custom_message:
            status_text = f"生产者{producer_index+1}{custom_message}"
        else:
            status_text = f"生产者{producer_index+1}已{'激活' if is_active else '停止'}"
            
        status_msg = QtWidgets.QGraphicsTextItem(status_text)
        status_msg.setFont(QtGui.QFont("Arial", 10, QtGui.QFont.Bold))
        status_msg.setDefaultTextColor(QtCore.Qt.darkBlue if is_active else QtCore.Qt.darkRed)
        status_msg.setPos(producer_x, producer_y - 20)
        
        # 添加到场景
        self.scene.addItem(status_msg)
        
        # 2秒后移除消息
        QtCore.QTimer.singleShot(2000, lambda: self.safe_remove_items([status_msg]))

    def generate_page_for_producer(self, producer_index):
        """根据生产模式为生产者生成页面"""
        if self.production_mode == "File" and self.file_page_sequence:
            # 从文件序列中获取下一个页面
            if self.file_page_index < len(self.file_page_sequence):
                page = self.file_page_sequence[self.file_page_index]
                self.file_page_index += 1
                
                # 文件中的页面已经是纯页号，直接使用
                page_number = page
                
                # 如果已经读取完所有页面，循环从头开始
                if self.file_page_index >= len(self.file_page_sequence):
                    self.file_page_index = 0
                    
                # 确保页号不超过16位
                return self.clean_page_number(page_number)
            else:
                # 如果没有文件序列，使用随机生成
                return self.clean_page_number(self.generate_random_page())
        else:
            # 生成一个随机页面号（已经是纯页号，不包含页内偏移）
            return self.clean_page_number(self.generate_random_page())
            
    def show_producer_queue_info(self, producer_index):
        """显示生产者队列信息"""
        if producer_index >= len(self.producer_queues):
            return
            
        queue_length = len(self.producer_queues[producer_index])
        if queue_length == 0:
            return
            
        # 移除旧的队列信息 - 避免重叠显示
        if hasattr(self, f'producer_{producer_index}_queue_items'):
            old_items = getattr(self, f'producer_{producer_index}_queue_items')
            self.safe_remove_items(old_items)
        
        # 创建新的队列项目列表
        queue_items = []
            
        # 计算生产者位置
        producer_x = 275  # 和draw_producers方法中一致
        producer_y = 130 + producer_index * 100
        producer_width = 160
        
        # 创建队列信息文本
        queue_text = f"队列: {queue_length}页待处理"
        queue_info = QtWidgets.QGraphicsTextItem(queue_text)
        queue_info.setFont(QtGui.QFont("Arial", 9))
        queue_info.setDefaultTextColor(QtCore.Qt.darkRed)
        queue_info.setPos(producer_x + producer_width + 5, producer_y + 30)
        
        # 添加到场景
        self.scene.addItem(queue_info)
        queue_items.append(queue_info)
        
        # 如果队列中有页面，在生产者旁边显示下一个待处理的页面
        if queue_length > 0:
            next_page = self.producer_queues[producer_index][0]
            
            # 创建页面箭头指向生产者
            arrow = QtWidgets.QGraphicsPolygonItem()
            polygon = QtGui.QPolygonF()
            # 箭头从右侧指向生产者
            arrow_start_x = producer_x + producer_width + 60
            arrow_start_y = producer_y + 40
            arrow_end_x = producer_x + producer_width
            arrow_end_y = producer_y + 40
            
            # 箭头主干
            polygon.append(QtCore.QPointF(arrow_start_x, arrow_start_y))
            polygon.append(QtCore.QPointF(arrow_end_x, arrow_end_y))
            
            # 箭头头部
            head_size = 10
            polygon.append(QtCore.QPointF(arrow_end_x + head_size, arrow_end_y - head_size/2))
            polygon.append(QtCore.QPointF(arrow_end_x, arrow_end_y))
            polygon.append(QtCore.QPointF(arrow_end_x + head_size, arrow_end_y + head_size/2))
            
            arrow.setPolygon(polygon)
            arrow.setPen(QtGui.QPen(QtCore.Qt.darkRed, 2))
            self.scene.addItem(arrow)
            queue_items.append(arrow)
            
            # 创建页面编号文本 - 使用十六进制格式显示，不足四位用0填充
            page_label = QtWidgets.QGraphicsTextItem(f"下一页: {next_page:04X}")
            page_label.setFont(QtGui.QFont("Arial", 9, QtGui.QFont.Bold))
            page_label.setDefaultTextColor(QtCore.Qt.darkRed)
            page_label.setPos(arrow_start_x - 30, arrow_start_y - 20)
            self.scene.addItem(page_label)
            queue_items.append(page_label)
            
            # 如果队列中有更多页面，显示其他页面的提示
            if queue_length > 1:
                other_pages_text = f"还有 {queue_length-1} 页等待处理"
                other_pages = QtWidgets.QGraphicsTextItem(other_pages_text)
                other_pages.setFont(QtGui.QFont("Arial", 8))
                other_pages.setDefaultTextColor(QtCore.Qt.darkRed)
                other_pages.setPos(arrow_start_x - 30, arrow_start_y + 5)
                self.scene.addItem(other_pages)
                queue_items.append(other_pages)
            
            # 存储队列项目，以便后续清除
            setattr(self, f'producer_{producer_index}_queue_items', queue_items)
            
            # 5秒后移除信息
            QtCore.QTimer.singleShot(5000, lambda: self.safe_remove_items(queue_items))
        else:
            # 存储队列项目，以便后续清除
            setattr(self, f'producer_{producer_index}_queue_items', queue_items)
            
            # 5秒后移除信息
            QtCore.QTimer.singleShot(5000, lambda: self.safe_remove_items(queue_items))

    def update_animation_speed(self, value):
        """更新页面从生产者到缓冲区的动画速度"""
        # 值越大，动画时间越短（越快）
        # 值范围100-1000，映射到动画时间15000-2000毫秒（反向映射）
        # 100 -> 15000ms (慢), 1000 -> 2000ms (快)
        animation_duration = int(15000 - (value - 100) * (15000 - 2000) / 900)
        self.page_animation_duration = animation_duration
        
        # 更新标签显示当前动画速度
        if hasattr(self.ui, 'label_12'):
            self.ui.label_12.setText(f"页面调入速度: {self.page_animation_duration/1000:.1f}秒")
        
        print(f"设置页面动画速度: {self.page_animation_duration}毫秒")

    def update_producer_speed_multiplier(self, value):
        """更新生产者生产速度倍数"""
        # 将滑块值转换为倍数 (50-200 -> 0.5-2.0)
        multiplier = value / 100.0
        self.producer_speed_multiplier = multiplier
        
        # 更新所有生产者的速度
        for i in range(self.producer_count):
            if i < len(self.producer_speeds) and i < len(self.original_producer_speeds):
                # 应用倍数到原始速度
                new_speed = int(self.original_producer_speeds[i] * multiplier)
                self.producer_speeds[i] = max(100, min(5000, new_speed))  # 限制在合理范围内
                
                # 如果生产者定时器已初始化，强制更新定时器间隔并重启（如果正在运行）
                if hasattr(self, 'producer_timers') and i < len(self.producer_timers):
                    was_active = self.producer_timers[i].isActive()
                    
                    # 停止定时器
                    if was_active:
                        self.producer_timers[i].stop()
                    
                    # 设置新的间隔
                    self.producer_timers[i].setInterval(self.producer_speeds[i])
                    
                    # 如果之前是活跃的，重新启动
                    if was_active and self.producers_active[i]:
                        self.producer_timers[i].start()
                    
                    # 输出调试信息
                    print(f"生产者{i+1}速度强制更新为: {self.producer_speeds[i]}ms (原始: {self.original_producer_speeds[i]}ms, 倍数: {multiplier:.1f})")
        
        # 更新标签
        if hasattr(self.ui, 'label_11'):
            avg_speed = sum(self.producer_speeds) / len(self.producer_speeds) if self.producer_speeds else 0
            self.ui.label_11.setText(f"生产者速度: {multiplier:.1f}倍")

    def add_page_to_tlb(self, page_number, rect_item, text_item):
        """将页面添加到TLB并清理动画项目"""
        try:
            # 计算纯页号
            pure_page_number = page_number >> self.page_offset_bits
            
            # 将页面加入TLB的展示
            scene_width = self.scene.width()
            tlb_x = int(scene_width * 0.9)
            tlb_y = 30
            
            # TLB中的条目显示
            if not hasattr(self, 'tlb_entries_display') or self.tlb_entries_display is None:
                self.tlb_entries_display = []
            
            # 清除动画项目
            if rect_item in self.scene.items():
                self.scene.removeItem(rect_item)
            if text_item in self.scene.items():
                self.scene.removeItem(text_item)
            
            # 确保清理所有临时项目
            if hasattr(self, 'temp_items') and self.temp_items:
                self.safe_remove_items(self.temp_items)
                self.temp_items = []
            
            # 检查页面是否已经在TLB中
            tlb_entry_exists = False
            for entry in self.tlb:
                if isinstance(entry, tuple) and entry[0] == pure_page_number:
                    tlb_entry_exists = True
                    break
                elif isinstance(entry, dict) and entry.get('page') == pure_page_number:
                    tlb_entry_exists = True
                    break
            
            # 如果页面不在TLB中，添加它
            if not tlb_entry_exists:
                # 查找页面在内存中的位置
                memory_index = -1
                for i, page in enumerate(self.memory):
                    if page == pure_page_number:
                        memory_index = i
                        break
                
                if memory_index != -1:
                    # 添加到TLB
                    if len(self.tlb) >= self.tlb_size:
                        self.tlb.pop()  # 移除最久未使用的条目
                    
                    # 使用字典格式添加新条目
                    self.tlb.insert(0, {
                        'page': pure_page_number,
                        'frame': memory_index,
                        'timestamp': self.total_count
                    })
            
            # 更新TLB的显示
            self.draw_tlb()
            
            # 显示消息
            message = f"页面 {pure_page_number:04X} 已添加到TLB"
            status_item = QtWidgets.QGraphicsTextItem(message)
            status_item.setFont(QtGui.QFont("Arial", 10))
            status_item.setDefaultTextColor(QtCore.Qt.darkGreen)
            status_item.setPos(tlb_x - 50, tlb_y + 200)
            self.scene.addItem(status_item)
            
            # 3秒后移除消息
            QtCore.QTimer.singleShot(3000, lambda: self.safe_remove_items([status_item]))
            
        except Exception as e:
            print(f"添加页面到TLB时出错: {e}")
            import traceback
            traceback.print_exc()

    def show_status_message(self, message, color="black", duration=2000):
        """显示状态消息"""
        try:
            # 获取场景宽度
            scene_width = self.scene.width()
            
            # 移除之前的状态消息
            if hasattr(self, 'status_message_item') and self.status_message_item in self.scene.items():
                self.scene.removeItem(self.status_message_item)
            
            # 创建新的状态消息
            self.status_message_item = QtWidgets.QGraphicsTextItem(message)
            self.status_message_item.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
            
            # 设置颜色
            if color == "red":
                self.status_message_item.setDefaultTextColor(QtCore.Qt.red)
            elif color == "green":
                self.status_message_item.setDefaultTextColor(QtCore.Qt.darkGreen)
            elif color == "blue":
                self.status_message_item.setDefaultTextColor(QtCore.Qt.blue)
            else:
                self.status_message_item.setDefaultTextColor(QtCore.Qt.black)
            
            # 放置在场景顶部中央
            text_width = self.status_message_item.boundingRect().width()
            self.status_message_item.setPos((scene_width - text_width) / 2, 10)
            
            # 添加到场景
            self.scene.addItem(self.status_message_item)
            
            # 设置定时器，在指定时间后移除消息
            QtCore.QTimer.singleShot(duration, lambda: self.safe_remove_items([self.status_message_item]))
        
        except Exception as e:
            print(f"显示状态消息时出错: {e}")
            import traceback
            traceback.print_exc()

    def remove_page_from_tlb(self, frame_index):
        """从TLB中移除与指定物理帧关联的页面"""
        try:
            to_remove = []
            
            # 找出所有与该物理帧关联的TLB条目
            for i, entry in enumerate(self.tlb):
                if isinstance(entry, tuple) and entry[1] == frame_index:
                    to_remove.append(i)
                elif isinstance(entry, dict) and entry.get('frame') == frame_index:
                    to_remove.append(i)
            
            # 从后向前移除（避免索引变化问题）
            for i in sorted(to_remove, reverse=True):
                # 获取要移除的页面号以便显示
                if isinstance(self.tlb[i], tuple):
                    page_num = self.tlb[i][0]
                else:
                    page_num = self.tlb[i].get('page', 0)
                    
                # 显示移除信息
                self.show_status_message(f"从TLB移除页面: {page_num:04X}", "blue", 1500)
                
                # 移除条目
                self.tlb.pop(i)
        
        except Exception as e:
            print(f"从TLB移除页面时出错: {e}")
            import traceback
            traceback.print_exc()

    def consume_page(self):
        """消费者从缓冲区取出一个页面进行处理"""
        try:
            # 检查消费者是否活跃
            if not self.consumer_active:
                return
            
            # 检查是否有动画正在进行
            if hasattr(self, 'animation_in_progress') and self.animation_in_progress:
                print("动画正在进行中，等待当前动画完成...")
                return
                
            # 检查缓冲区是否为空
            if not self.buffer:
                # 缓冲区为空，无法消费
                self.show_status_message("消费者等待：缓冲区为空", "red", 2000)
                return
            
            # 从缓冲区获取一个页面
            page_number = self.buffer.pop(0)
            
            # 检查页面号的类型，如果是元组，则提取第一个元素作为页面号
            if isinstance(page_number, tuple):
                page_number = page_number[0]
            
            # 确保页号不超过16位
            page_number = self.clean_page_number(page_number)
            
            # 页面号右移页内偏移位数，得到纯页号
            pure_page_number = page_number >> self.page_offset_bits
            
            # 更新缓冲区显示
            self.draw_buffer()
            
            # 标记动画正在进行中 - 提前设置标记，防止在查看是否有空闲生产者时立即恢复生产者
            self.animation_in_progress = True
            
            # 查看是否有空闲生产者需要恢复 - 由于已设置animation_in_progress，生产者不会立即恢复
            for i in range(len(self.producers_active)):
                if i < len(self.producer_current_pages) and self.producers_active[i] and self.producer_current_pages[i] is None:
                    self.check_buffer_and_resume(i)
            
            # 检查TLB是否命中
            tlb_hit = False
            memory_index = -1
            tlb_update_info = {}  # 存储TLB更新信息，用于动画完成后更新
            
            # 创建页表如果不存在
            if not hasattr(self, 'page_table') or self.page_table is None:
                self.page_table = {}
            
            # 创建TLB如果不存在
            if not hasattr(self, 'tlb') or self.tlb is None:
                self.tlb = []
            
            # 遍历TLB查找页面
            for i, entry in enumerate(self.tlb):
                if isinstance(entry, tuple):
                    tlb_page, tlb_frame = entry
                    if tlb_page == pure_page_number:
                        tlb_hit = True
                        memory_index = tlb_frame
                        
                        # 记录TLB更新信息，但不立即更新
                        tlb_update_info = {
                            'hit': True,
                            'hit_index': i,
                            'entry': entry
                        }
                        break
                elif isinstance(entry, dict):
                    if entry.get('page') == pure_page_number:
                        tlb_hit = True
                        memory_index = entry.get('frame')

                        # 记录TLB更新信息，但不立即更新
                        tlb_update_info = {
                            'hit': True,
                            'hit_index': i,
                            'entry': entry
                        }
                        break
            
            # 如果TLB未命中，查询页表
            if not tlb_hit:
                # 查看页表中是否有该页面的映射
                if pure_page_number in self.page_table:
                    memory_index = self.page_table[pure_page_number]
                    
                    # 确认内存中该位置有正确的页面
                    if memory_index < len(self.memory) and self.memory[memory_index] == pure_page_number:
                        # 页表命中 - 记录TLB更新信息，但不立即更新
                        self.show_status_message(f"页表命中: 页面 {pure_page_number:04X} 在帧 {memory_index}", "green", 2000)
                        
                        # 记录需要添加到TLB的信息
                        tlb_update_info = {
                            'hit': False,
                            'add_entry': {
                                'page': pure_page_number, 
                                'frame': memory_index,
                                'timestamp': self.total_count
                            }
                        }
                    else:
                        # 页表中的映射无效，需要重新分配
                        memory_index = -1
                        # 从页表中移除无效的映射
                        if pure_page_number in self.page_table:
                            del self.page_table[pure_page_number]
                else:
                    # 页表缺失 - 需要选择物理帧
                    memory_index = -1
                
                # 如果页表缺失或映射无效，需要分配物理帧
                if memory_index == -1:
                    # 检查该页面是否已经在内存中（在不同的帧）
                    found = False
                    for idx, page in enumerate(self.memory):
                        if page == pure_page_number:
                            memory_index = idx
                            found = True
                            self.show_status_message(f"页面 {pure_page_number:04X} 已在内存中的帧 {memory_index}", "green", 2000)
                            break
                    
                    if not found:
                        # 页面不在内存中，增加缺页计数
                        self.miss_count += 1
                        print(f"缺页：页面 {pure_page_number:04X} 不在内存中，缺页数 +1 = {self.miss_count}")
                        self.show_status_message(f"缺页：页面 {pure_page_number:04X} 不在内存中", "red", 2000)
                        
                        # 首先检查是否有空闲的内存帧
                        free_frame_index = -1
                        for i, page in enumerate(self.memory):
                            if page is None:
                                free_frame_index = i
                                break
                        
                        # 如果有空闲帧，使用它
                        if free_frame_index != -1:
                            memory_index = free_frame_index
                            self.show_status_message(f"页面 {pure_page_number:04X} 放入空闲帧 {memory_index}", "green", 2000)
                        else:
                            # 没有空闲帧，使用替换算法选择牺牲页
                            victim_index = self.select_victim_page()
                            
                            # 如果没有可用的内存帧或内存为空，分配新帧
                            if victim_index == -1 or not self.memory:
                                # 添加新帧，但不超过内存块数量限制
                                if len(self.memory) < self.memory_blocks:
                                    victim_index = len(self.memory)
                                    self.memory.append(None)
                                    self.show_status_message(f"添加新内存帧: {victim_index}", "blue", 2000)
                                else:
                                    # 如果已达到内存块数量限制，使用替换算法
                                    victim_index = self.select_victim_page()
                                    self.show_status_message(f"内存已满，使用替换算法选择帧: {victim_index}", "orange", 2000)
                            
                            memory_index = victim_index
                            
                            # 显示页面替换信息
                            victim_page = self.memory[victim_index] if victim_index < len(self.memory) else None
                            if victim_page is not None:
                                # 使用纯页号显示
                                if isinstance(victim_page, int):
                                    victim_pure_page = victim_page
                                elif isinstance(victim_page, tuple):
                                    victim_pure_page = victim_page[0]
                                else:
                                    victim_pure_page = victim_page
                                    
                                # 显示替换信息
                                # self.show_replacement_info(victim_pure_page, pure_page_number, victim_index)
                                
                                # 记录需要从TLB中移除的信息，但不立即移除
                                tlb_update_info['remove_frame'] = victim_index
                                
                                # 记录需要从页表中移除的信息，但不立即移除
                                tlb_update_info['remove_page_table'] = victim_pure_page
                    
                    # 将新页面添加到页表 - 确保一个物理帧只映射一个页面
                    # 这个操作可以立即执行，因为它不会影响动画
                    self.page_table[pure_page_number] = memory_index
                    
                    # 记录需要添加到TLB的信息，但不立即添加
                    tlb_update_info['add_entry'] = {
                        'page': pure_page_number, 
                        'frame': memory_index,
                        'timestamp': self.total_count
                    }
        
            # 确保内存有足够空间，但不超过内存块数量限制
            while len(self.memory) <= memory_index and len(self.memory) < self.memory_blocks:
                self.memory.append(None)
            
            # 保存页面和内存索引，等动画完成后再更新内存
            self.pending_memory_update = {
                'page_number': pure_page_number,
                'memory_index': memory_index,
                'tlb_update_info': tlb_update_info  # 添加TLB更新信息
            }
            
            # 暂停消费者定时器，确保一次只处理一个页面
            if hasattr(self, 'consumer_timer') and self.consumer_timer.isActive():
                print(f"暂停消费者定时器，等待页面 {pure_page_number:04X} 动画完成")
                self.consumer_timer.stop()
            
            # 创建一个回调函数，在动画完成后恢复消费者定时器
            def animation_complete_callback():
                # 更新内存
                self.update_memory_after_animation()
                
                # 标记动画已完成
                self.animation_in_progress = False
                
                # 恢复消费者定时器
                if hasattr(self, 'consumer_timer') and self.consumer_active:
                    print(f"页面 {pure_page_number:04X} 动画完成，恢复消费者定时器")
                    self.consumer_timer.start()
                
                # 现在动画已完成，检查是否有生产者需要恢复
                for i in range(len(self.producers_active)):
                    if i < len(self.producer_current_pages) and self.producers_active[i] and self.producer_current_pages[i] is None:
                        # 使用更长的延迟确保状态完全恢复
                        QtCore.QTimer.singleShot(500, lambda idx=i: self.check_buffer_and_resume(idx))
            
            # 动画显示页面消费过程
            self.animate_page_consumption(0, page_number, memory_index, tlb_hit, animation_complete_callback)
            
            # 更新TLB命中率
            self.tlb_hit_count = self.tlb_hit_count + 1 if tlb_hit else self.tlb_hit_count
            self.total_count += 1
            
            # 更新标签
            self.update_labels()
            
        except Exception as e:
            print(f"消费者处理错误：{e}")
            import traceback
            traceback.print_exc()
            
            # 确保出错时也能恢复消费者定时器和清除动画标记
            self.animation_in_progress = False
            if hasattr(self, 'consumer_timer') and self.consumer_active:
                self.consumer_timer.start()

    def update_memory_after_animation(self):
        """动画完成后更新内存"""
        if hasattr(self, 'pending_memory_update'):
            memory_index = self.pending_memory_update.get('memory_index')
            pure_page_number = self.pending_memory_update.get('page_number')
            tlb_update_info = self.pending_memory_update.get('tlb_update_info', {})
            
            print(f"动画完成后更新内存: 页面 {pure_page_number:04X} -> 帧 {memory_index}")
            
            if memory_index is not None and pure_page_number is not None:
                # 将页面放入内存 - 存储纯页号
                if memory_index < len(self.memory):
                    # 保存旧页面号用于调试
                    old_page = self.memory[memory_index]
                    
                    # 更新内存
                    self.memory[memory_index] = pure_page_number
                    
                    print(f"内存帧 {memory_index} 更新: {old_page if old_page is not None else 'None'} -> {pure_page_number:04X}")
                    
                    # 更新页面访问记录
                    self.page_last_used[pure_page_number] = self.total_count  # 更新最后访问时间
                    self.page_reference_counts[pure_page_number] = self.page_reference_counts.get(pure_page_number, 0) + 1  # 更新访问计数
                    
                    # 如果使用FIFO算法，确保页面索引在队列中
                    if self.replacement_algorithm == "FIFO":
                        # 初始化FIFO队列如果不存在
                        if not hasattr(self, 'fifo_queue') or self.fifo_queue is None:
                            self.fifo_queue = []
                        
                        # 如果是旧页面被替换，从队列中移除旧页面索引
                        if old_page is not None and memory_index in self.fifo_queue:
                            self.fifo_queue.remove(memory_index)
                        
                        # 将新页面索引添加到队列末尾
                        if memory_index not in self.fifo_queue:
                            self.fifo_queue.append(memory_index)
                    
                    # 现在处理TLB更新
                    if tlb_update_info:
                        # 如果是TLB命中，更新LRU信息
                        if tlb_update_info.get('hit'):
                            hit_index = tlb_update_info.get('hit_index')
                            entry = tlb_update_info.get('entry')
                            if hit_index is not None and hit_index < len(self.tlb) and entry:
                                # 更新LRU信息（将命中的条目移到TLB的最前面）
                                print(f"TLB命中，更新LRU信息: 将条目 {hit_index} 移到最前面")
                                try:
                                    hit_entry = self.tlb.pop(hit_index)
                                    self.tlb.insert(0, hit_entry)
                                except Exception as e:
                                    print(f"更新TLB LRU信息时出错: {e}")
                        
                        # 如果需要从TLB中移除某个帧的映射
                        if 'remove_frame' in tlb_update_info:
                            victim_frame = tlb_update_info['remove_frame']
                            print(f"从TLB中移除帧 {victim_frame} 的映射")
                            self.remove_page_from_tlb(victim_frame)
                        
                        # 如果需要从页表中移除某个页面的映射
                        if 'remove_page_table' in tlb_update_info:
                            victim_page = tlb_update_info['remove_page_table']
                            for p, f in list(self.page_table.items()):
                                if f == memory_index:
                                    print(f"从页表中移除映射: 页面 {p:04X} -> 帧 {f}")
                                    del self.page_table[p]
                                    break
                        
                        # 如果需要添加新的TLB条目
                        if 'add_entry' in tlb_update_info:
                            new_entry = tlb_update_info['add_entry']
                            if new_entry:
                                # 检查TLB是否已满
                                if len(self.tlb) >= self.tlb_size:
                                    old_entry = self.tlb.pop()  # 移除最久未使用的TLB条目
                                    if isinstance(old_entry, dict):
                                        print(f"TLB已满，移除最久未使用的条目: 页面 {old_entry.get('page', 0):04X}")
                                        self.show_status_message(f"从TLB移除页面: {old_entry.get('page', 0):04X}", "blue", 1500)
                                
                                # 添加新的TLB条目
                                print(f"向TLB添加新条目: 页面 {new_entry.get('page', 0):04X} -> 帧 {new_entry.get('frame', 0)}")
                                self.tlb.insert(0, new_entry)
                
                    # 重新绘制内存和TLB
                    self.draw_memory()
                    self.draw_tlb()
                else:
                    print(f"警告: 内存索引 {memory_index} 超出内存范围 {len(self.memory)}")
            else:
                print(f"错误: 无效的内存更新信息 - 索引: {memory_index}, 页面: {pure_page_number}")
            
            # 清除待处理的内存更新信息
            self.pending_memory_update = None
        else:
            print("警告: 没有待处理的内存更新信息")


if __name__ == "__main__":
    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
    # 创建应用程序实例
    app = QtWidgets.QApplication(sys.argv)
    # 创建主窗口实例
    window = mainUI()
    window.show()
    sys.exit(app.exec_())
